How to Connect Cowboy (Erlang) websocket to webflow.io generated webpage - websocket

I’m want to use cowboy websocket Interface to interact with a webpage generated with webflow. I would like to have an simple example how to add javascript implementing websocket connected to webflow HTML.

This should get you started:
Drag a Container, which is a div, onto your page. Give it the ID server_info.
Drag a Button onto your page. Give it the ID get_server_info.
The Page Settings allow you to add javascript to a page. The js for connecting to a server with a websocket will look something like this:
<script>
var ws = new WebSocket("ws://localhost:8080/please_upgrade_to_websocket");
ws.onerror = function(event) {
console.error("[ME]WebSocket error observed:", event);
};
ws.onclose = function(event) {
console.log("[ME]WebSocket is closed now.");
};
$("#get_server_info").click(function() {
if(ws.readyState == 1) {
ws.send("My data");
}
else {
alert("Websocket not open! readyState: " + ws.readyState);
}
});
ws.onmessage = function (event) {
alert("Data was retrieved from server and will be inserted in page.");
$("#server_info").text(event.data);
};
</script>
Create a new cowboy app called hello_erlang.
Create some routes and and specify their handler functions by putting the following in hello_erlang/src/hello_erlang_app.erl:
-module(hello_erlang_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
HelloRoute = { "/", hello_handler, [] },
WebSocketRoute = {"/please_upgrade_to_websocket", myws_handler, []},
CatchallRoute = {"/[...]", no_matching_route_handler, []},
Dispatch = cowboy_router:compile([
{'_', [HelloRoute, WebSocketRoute, CatchallRoute]}
]),
{ok, _} = cowboy:start_clear(my_http_listener,
[{port, 8080}],
#{env => #{dispatch => Dispatch} }
),
hello_erlang_sup:start_link().
stop(_State) ->
ok.
To serve the simple html page with the button, I used hello_erlang/src/hello_handler.erl:
-module(hello_handler).
-behavior(cowboy_handler).
-export([init/2]).
init(Req0, State) ->
io:format("[ME]Entered hello_handler~n"),
Body = my_get_file("html/form.htm"), %% Body can be a binary() or an iolist() (which is a list containing integers, strings, or binaries)
Req = cowboy_req:reply(200,
#{<<"content-type">> => <<"text/html">>},
Body,
Req0),
{ok, Req, State}.
my_get_file(Path) ->
PrivDir = code:priv_dir(hello_erlang), %% Finds the path of an application's priv directory
AbsPath = filename:join([PrivDir, Path]),
case file:read_file(AbsPath) of
{ok, Bin} -> Bin;
_ -> ["<div>Cannot read file: ", Path, "</div>"] %% iolist()
end.
According to the erlang docs, the priv directory is where application specific files should go. Below is the form.htm page, which I put it in the directory hello_erlang/priv/html/ (I created the html directory). In the <head> section of the html page there is a <script> tag which links to the jquery library, which is used by the javascript:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous">
</script>
</head>
<body>
<div id="server_info">Server info</div>
<button type="button" id="get_server_info">Get sever info</button>
<script>
var my_websocket = new WebSocket("ws://localhost:8080/please_upgrade_to_websocket");
my_websocket.onerror = function(event) {
console.error("[ME]WebSocket error observed:", event);
};
my_websocket.onclose = function(event) {
console.log("[ME]WebSocket is closed now.");
};
$("#get_server_info").click(function() {
if(my_websocket.readyState == 1) {
my_websocket.send("My data");
}
else {
alert("Websocket not open! readyState: " + my_websocket.readyState );
}
});
my_websocket.onmessage = function (event) {
$("#server_info").text(event.data);
alert("Data was retrieved from server and will be inserted in page.");
};
</script>
</body>
</html>
Edit: Another way to serve the html file is to do away with hello_handler.erl and set the route like this:
HelloRoute = { "/", cowboy_static, {priv_file, hello_erlang, "html/form.htm"} },
You would put form.htm in the directory hello_erlang/priv/html/ (I created the html directory). See the cowboy docs on static files. When you don't need to use erlang to alter the html file in some way before sending it to the client, that is the simplest way to serve the file.
hello_erlang/src/myws_handler.erl:
-module(myws_handler).
-export([init/2, websocket_init/1, websocket_handle/2, websocket_info/2]).
init(Req, State) ->
{cowboy_websocket, Req, State}. %Perform websocket setup
websocket_init(State) ->
io:format("[ME]: Inside websocket_init()~n"),
{ok, State}.
websocket_handle({text, Msg}, State) ->
{Hours, Minutes, Secs} = time(),
{
reply,
{text, io_lib:format("[~w:~w:~w]: Server received: ~s", [Hours, Minutes, Secs, Msg]) },
State
};
websocket_handle(_Other, State) -> %Ignore
{ok, State}.
websocket_info({text, Text}, State) ->
{reply, {text, Text}, State};
websocket_info(_Other, State) ->
{ok, State}.
hello_erlang/src/no_matching_route_handler.erl:
-module(no_matching_route_handler).
-behavior(cowboy_handler).
-export([init/2]).
init(Req0, State) -> %State comes from last argument of route
Req = cowboy_req:reply(404,
#{<<"content-type">> => <<"text/plain">>},
<<"[ME] 404. Whoops! (No matching route!)">>,
Req0),
{ok, Req, State}.
Then, in the top level directory of your app, hello_erlang in this case, start your cowboy server:
...cowboy_apps/hello_erlang$ make run
Then, enter the following url in your browser:
http://localhost:8080/
That will cause cowboy to serve up the html page containing the button. Clicking on the button will send some data to the server using the websocket, and the server will respond, then the js will insert the response in the web page.
Webflow is really irrelevant to the answer: it doesn't matter how or what you use to create html and javascript, the end product is an html page, which you put somewhere in your server's directory. When a browser requests the html file from your server, the server sends the html file to the browser, then the browser reads the file and produces the pretty text and pictures you see, and the browser executes the javascript at the appropriate time. A browser has no idea who are what created the html and javascript in the file.

Related

Permanently change html file using AJAX call on node.js server

How do I write a script to permanently change a static html file after making an ajax call to the node.js server? Any examples would be greatly appreciated :)
I agree with NikxDa that this is probably not the best solution for you, but this code should do the trick.
/write.js
var http = require('http');
var fs = require('fs');
var url = require('url');
//Lets define a port we want to listen to
const PORT=8080;
function handleRequest(request, response){
var path = url.parse(request.url).pathname;
if(path=="/write"){
fs.appendFile('message.html', 'Node.js!', function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
} else {
fs.readFile('index.html',function (err, data){
response.writeHead(200, {'Content-Type': 'text/html','Content-Length':data.length});
response.write(data);
response.end();
});
}
}
// Create a server.
var server = http.createServer(handleRequest);
server.listen(PORT, function(){
console.log("Server listening on: http://localhost:%s", PORT);
});
/index.html
<!DOCTYPE html>
<head>
<script>
function writeIt()
{
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","http://localhost:8080/write", true);
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
string=xmlhttp.responseText;
document.write(string + ": Saved change to message.html");
}
}
xmlhttp.send();
}
</script>
</head>
<body>
<p>Click the button to send an AJAX request to `write.js`<p>
<br><button onclick="writeIt()">Click Me</button>
</body>
/message.html
Node.js!
Editing the file directly via node would probably be really bad, I do not even know if it is at all possible. I think the better solution is for your Node Server to make the data you want to change accessible and then use jQuery or Angular to update the HTML-File when it is actually loaded.
Another approach would be to use a templating engine like https://github.com/tj/ejs, and then serve the file via Node directly, so you can change the data in the Node-Application itself every time.

Accessing socket.io in koa route

I'm trying to use socket.io with koa.js and I was able to connect adding server = require('http').createServer(koa.callback()).listen(port); and io = require('socket.io')(server); at the very bottom of my application but now I want to emit and if possible listen to events from my controller / route. What's the best way to implement this?
I've tried adding io in my koa context like koa.context.io = io and even io.on('connection', function(socket){ koa.context.socket = socket }); but nothing is working.
Thanks in advance guys.
Accessing the socket.io instance in your koa route should not work.
Creating the socket.io instance depends on the application creating a callback function that can be used by the http server.
var server = http.createServer(app.callback());
var io = require('socket.io')(server);
This callback is generated with the help of co and requires that your app is already set up with all the middleware/routes. (see the koa source). Therefore you can't use the socket.io instance (which is created afterwards) in those routes.
Furthermore I think it is not intended to emit socket.io events in your controllers. If you want to send data back to the client that called the controller, you should do it in the response which is generated by that controller. If you want to emit further events at the server you could trigger them from the client by emitting an event that the server will receive. This way you can process the data from the client in the function you pass to socket.on(...) and don't need to implement it in the controller/routes for koa.
Here is an example for the second case, without any koa controller/route.
app.js:
var http = require('http');
var koa = require('koa');
var app = koa();
var send = require('koa-send');
app.use(function* (next) {
if (this.path !== '/') return yield next;
yield send(this, __dirname + '/index.html');
});
var server = http.createServer(app.callback());
var io = require('socket.io')(server);
io.on('connection', function (socket) {
socket.on('click', function (data) {
//process the data here
console.log('client clicked! data:');
console.log(data);
// emit an event
console.log('responding with news');
socket.emit('news', { hello: 'world' });
});
});
var port = process.env.PORT || 3000;
server.listen(port);
console.log ('Listening at port ' + port + ' ...');
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>koa-socket.io</title>
</head>
<body>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost:3000');
socket.on('news', function (data) {
console.log('received news with data: ');
console.log(data);
});
function myclick () {
console.log("click");
socket.emit('click', { clickdata: 'i clicked the button' });
}
</script>
<button type="button" onclick="myclick();">Click Me and watch console at server and in browser.</button>
</body>
</html>
I realise this is a little late on the uptake, and could be deemed slightly self-serving as I'm going to suggest one of my own modules, but, you're on the right track with appending it to the app, with Koa v2 this is easier as the context is passed right along but with v1 you can tack it onto this, as koa middleware's are bound to the app instance.
Alternatively, I wrote a module to help with this exact use-case, https://github.com/mattstyles/koa-socket, it does just 2 things currently (and probably forever): it appends the socket.io server instance to the context and it allows you to write koa-style middleware for your socket listeners.

Fetch terminal command (Nodejs) and send to specific port via AJAX

I'm actually working on a little application. I have one server written in C which is listening on the port 5260. In the other side I have a NodeJS client which is listening on the port 7777. A HTML page can be reach via this port. In the HTML page I have a simple button.
When I click on this one a message is sent to my NodeJS server and is written on the terminal. Now I would like to fetch this command and send it to my C server which is still running and waiting for a request.
My client.js :
var http = require('http');
var ejs = require('ejs');
var express=require('express');
var app = express();
app.engine('html', ejs.renderFile);
app.set('/', __dirname);
app.get('/', function(request,response) {
response.render('index.ejs.html');
})
var options = {
host: '192.168.1.154',
path: '/',
port: '5260',
method: 'POST'
};
app.post('/play', function(req, res){
var res = http.request(options);
console.log("START_BG;BG1\n");
});
app.listen(7777);
And my HTML file :
<html>
<head>
<script type="text/javascript" src="client.js"></script>
<script type="text/javascript">
function sendMessage() {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/play', true);
xhr.onload = function() {
console.log(xhr);
};
xhr.send();
}
</script>
</head>
<body>
<button onclick="sendMessage()">VIDEO</button>
</body>
</html>
Well. I see some strange things in your code
1 You're making the post request to 192.168.1.254:5620/play without sending any data on it
2 You're not waiting fro the request to end and blindly print on your console without checking the result. Don't know if its the desired behaviour, but it seems a bit strange
Without more knowledge about the scenario is difficult to suggest an idea.
What is the answer you expect from the remote server?
It's suposed to print something in the (remote) console ?
What it should return via HTTP ?
Anyway I suggest you correct your code as follows:
app.post('/play', function(req, res){
var res = http.request(options, function(response){
// do any checking about response status and/or body if any
console.log("START_BG;BG1\n");
});
});

a file upload progress bar with node (socket.io and formidable) and ajax

I was in the middle of teaching myself some Ajax, and this lesson required building a simple file upload form locally. I'm running XAMPP on windows 7, with a virtual host set up for http://test. The solution in the book was to use node and an almost unknown package called "multipart" which was supposed to parse the form data but was crapping out on me.
I looked for the best package for the job, and that seems to be formidable. It does the trick and my file will upload locally and I get all the details back through Ajax. BUT, it won't play nice with the simple JS code from the book which was to display the upload progress in a progress element. SO, I looked around and people suggested using socket.io to emit the progress info back to the client page.
I've managed to get formidable working locally, and I've managed to get socket.io working with some basic tutorials. Now, I can't for the life of me get them to work together. I can't even get a simple console log message to be sent back to my page from socket.io while formidable does its thing.
First, here is the file upload form by itself. The script inside the upload.html page:
document.getElementById("submit").onclick = handleButtonPress;
var httpRequest;
function handleResponse() {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
document.getElementById("results").innerHTML = httpRequest.responseText;
}
}
function handleButtonPress(e) {
e.preventDefault();
var form = document.getElementById("myform");
var formData = new FormData(form);
httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = handleResponse;
httpRequest.open("POST", form.action);
httpRequest.send(formData);
}
And here's the corresponding node script (the important part being form.on('progress')
var http = require('http'),
util = require('util'),
formidable = require('formidable');
http.createServer(function(req, res) {
if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
var form = new formidable.IncomingForm(),
files = [],
fields = [];
form.uploadDir = './files/';
form.keepExtensions = true;
form
.on('progress', function(bytesReceived, bytesExpected) {
console.log('Progress so far: '+(bytesReceived / bytesExpected * 100).toFixed(0)+"%");
})
.on('file', function(name, file) {
files.push([name, file]);
})
.on('error', function(err) {
console.log('ERROR!');
res.end();
})
.on('end', function() {
console.log('-> upload done');
res.writeHead(200, "OK", {
"Content-Type": "text/html", "Access-Control-Allow-Origin": "http://test"
});
res.end('received files: '+util.inspect(files));
});
form.parse(req);
} else {
res.writeHead(404, {'content-type': 'text/plain'});
res.end('404');
}
return;
}).listen(8080);
console.log('listening');
Ok, so that all works as expected. Now here's the simplest socket.io script which I'm hoping to infuse into the previous two to emit the progress info back to my page. Here's the client-side code:
var socket = io.connect('http://test:8080');
socket.on('news', function(data){
console.log('server sent news:', data);
});
And here's the server-side node script:
var http = require('http'),
fs = require('fs');
var server = http.createServer(function(req, res) {
fs.createReadStream('./socket.html').pipe(res);
});
var io = require('socket.io').listen(server);
io.sockets.on('connection', function(socket) {
socket.emit('news', {hello: "world"});
});
server.listen(8080);
So this works fine by itself, but my problem comes when I try to place the socket.io code inside my form.... I've tried placing it anywhere it might remotely make sense, i've tried the asynchronous mode of fs.readFile too, but it just wont send anything back to the client - meanwhile the file upload portion still works fine. Do I need to establish some sort of handshake between the two packages? Help me out here. I'm a front-end guy so I'm not too familiar with this back-end stuff. I'll put this aside for now and move onto other lessons.
Maybe you can create a room for one single client and then broadcast the percentage to this room.
I explained it here: How to connect formidable file upload to socket.io in Node.js

Dart How to code a simple web-socket echo app

I've been attempting to learn enough html, css, and Dart to create my first web page and all is going well, except that I do not understand how to create a simple page and a server side web-socket server that will just echo it back. The examples that I find tend to illustrate other Dart tools and either connect to echo server on the web or do other things that make their code not simple for a newbie.
I've tried to simplify Seth Ladd's example "dart-example-web-sockets-client" as the 'best' example. I can receive what is sent from the page, repackage it and think i'm sending it back but absolutely nothing happens on the web page. I start the page by clicking on the URL returned when the web-server is run from inside the Dart editor. Since the page is not, AFAIK, run in the debugger I'm hampered in diagnosing the error.
Here is simplified code from Seth's server:
void handleEchoWebSocket(WebSocket webSocket) {
log.info('New WebSocket connection');
// Listen for incoming data. We expect the data to be a JSON-encoded String.
webSocket
.map((string) => JSON.decode(string))
.listen((json) {
// The JSON object should contain a 'request' entry.
var request = json['request'];
switch (request) {
case 'search':
var input = json['input'];
log.info("Received request '$request' for '$input'");
var response = {
'response': request,
'input': input,
};
webSocket.add(JSON.encode(response)); // can't detect page receiving this.
log.info("Echoed request..$request $input"); // correct data
break;
default:
log.warning("Invalid request: '$request'");
}
}, onError: (error) {
log.warning('Bad WebSocket request');
});
}
This example took the user input using it as input to two search engines, packaged the results and returned them to the page for display creating new DOM elements on the fly.
I just need to be pointed to a simple example that will echo what is submitted.
Here is a simple websocket client/server echo example. Messages doesn't show in browser window, but they are printed in console window. You have to start server.dart and main.dart separately. Both processes print messages to their own console window.
Edit: I added an output div for displaying the message also in browser.
bin\ws_server.dart:
import "dart:convert";
import 'dart:io';
import 'package:route/server.dart' show Router;
void handleWebSocket(WebSocket webSocket) {
// Listen for incoming data. We expect the data to be a JSON-encoded String.
webSocket
.map((string)=> JSON.decode(string))
.listen((json) {
// The JSON object should contains a 'echo' entry.
var echo = json['echo'];
print("Message to be echoed: $echo");
var response='{"response": "$echo"}';
webSocket.add(response);
}, onError: (error) {
print('Bad WebSocket request');
});
}
void main() {
int port = 9223;
HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then((server) {
print("Search server is running on "
"'http://${server.address.address}:$port/'");
var router = new Router(server);
// The client will connect using a WebSocket. Upgrade requests to '/ws' and
// forward them to 'handleWebSocket'.
router.serve('/ws')
.transform(new WebSocketTransformer())
.listen(handleWebSocket);
});
}
web\index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Websocket echo</title>
</head>
<body>
<p>Websocket test</p>
<div id="output"></div>
<script type="application/dart" src="main.dart"></script>
</body>
</html>
web\main.dart:
library main;
import 'dart:async';
import 'dart:convert';
import 'dart:html';
class WebsocketService {
WebSocket webSocket;
WebsocketService() {
connect();
}
void connect() {
webSocket = new WebSocket('ws://127.0.0.1:9223/ws');
webSocket.onOpen.first.then((_) {
onConnected();
sendws("Hello websocket server");
webSocket.onClose.first.then((_) {
print("Connection disconnected to ${webSocket.url}");
onDisconnected();
});
});
webSocket.onError.first.then((_) {
print("Failed to connect to ${webSocket.url}. "
"Please run bin/server.dart and try again.");
onDisconnected();
});
}
void onConnected() {
webSocket.onMessage.listen((e) {
onMessage(e.data);
});
}
void onDisconnected() {
print("Disconnected, trying again in 3s");
new Timer(new Duration(seconds:3), (){
connect();
});
}
void onMessage(data) {
var json = JSON.decode(data);
var echoFromServer = json['response'];
print("Received message: $echoFromServer");
var output=querySelector('#output');
output.text="Received message: $echoFromServer";
new Timer(new Duration(seconds:3), (){ //Send a new message to server after 3s
String now = new DateTime.now().toString();
sendws("Time: $now");
});
}
void sendws(String msg){
var request = '{"echo": "$msg"}';
print("Send message to server: $request");
webSocket.send(request);
}
}
void main() {
WebsocketService ws=new WebsocketService();
}

Resources