How to make the client receive server sent event using Javalin? - javascript

I am trying to implement a server client project which needs the server to send data to the client every 5 minutes with the client only asking in the beginning of connection. Server-sent events seem to be the go-to solution.
I have tried to use the functions given in the Javalin Documents. I am able to receive a response with a simple get from the server. But I couldn't establish a sse connection. The code enters the lambda function in the server but the client does not receive anything. I am not sure if the client or the server, or even both have a problem.
The only output we get from the codes below is "connected" on the server side. Thank you in advance.
Code for the server
import io.javalin.Javalin;
public class SimpleTwitter {
public static void main(String[] args) {
Javalin app = Javalin.create().start(7000);
app.sse("/sse", client ->{
System.out.println("connected");
client.sendEvent("message","Hello, SSE");
client.onClose(() -> System.out.println("Client disconnected"));
});
app.get("/", ctx -> ctx.result("Hello World"));
}
}
Code for the client
<!DOCTYPE html>
<html>
<body>
<h1>Getting server updates</h1>
<div id="result"></div>
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("http://localhost:7000/sse");
source.onmessage = function(event) {
document.getElementById("result").innerHTML += event.data + "<br>";
};
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
}
</script>
</body>
</html>

Turns out the problem was not the code. By looking at the developer tools on chrome, we saw the following:
"Access to resource at 'http://localhost:7000/sse' from origin 'null'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource."
When we installed a chrome extention called "Allow-Control-Allow-Origin: *", we were able to see the output.
Also, here are the updated better working codes:
> <<!DOCTYPE html> <html> <body>
>
> <h1>Tweets</h1>
>
> <script> new
> EventSource('http://localhost:7000/sse').addEventListener( "hi", msg
> =>{ document.write(msg.data); }); </script>
>
> </body> </html>
...
public static void main(String[] args) throws InterruptedException {
Queue<SseClient> clients = new ConcurrentLinkedQueue<>();
Javalin app = Javalin.create().start(7000);
app.sse("/sse", client -> {
clients.add(client);
client.onClose(() -> clients.remove(client));
});
while (true) {
for (SseClient client : clients) {
client.sendEvent("hi", "hello world");
}
TimeUnit.SECONDS.sleep(1);
}
}

Related

Subscribe to Kafka Topics over websocket from client side

We are trying to listen to the kafka topics using js code from browser once the Producer from server side pushes messages to the particular kafka topic.
In the server side, kafka server and zookeeper are running at 9092 and 2181 port respectively.
String topicName = "test";
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "all");
props.put("retries", 0);
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);
props.put("key.serializer",
"org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer",
"org.apache.kafka.common.serialization.StringSerializer");
props.put("partitioner.class",
"org.apache.kafka.clients.producer.internals.DefaultPartitioner");
Thread.currentThread().setContextClassLoader(null);
Producer<String, String> producer = new KafkaProducer <String, String>(props);
for(int i = 0; i < 10; i++){
producer.send(new ProducerRecord<String, String>(topicName,
Integer.toString(i), Integer.toString(i) + i));
}
producer.close();
}catch(Exception e){
e.printStackTrace();
}
Client code snippet:
<!DOCTYPE html>
<html>
<head>My Page</head>
<script src="stomp.js"></script>
<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
<script type="text/javascript">
console.log('Starting: ');
var socket = new SockJS('ws://localhost:9092');
client = Stomp.over(socket);
client.connect( "", "",
function() {
console.log('Connected: ');
client.subscribe("/topic/test",
function( message ) {
alert( message );
}
);
}
);
</script>
</html>
From client side, while we are trying to connect via ws, only the Starting: console is getting printed and Connected: is not getting rpinted since the
websocket connection to the kafka server is not getting succeeded.
Since STOMP is not directly supported for Kafka, we tried to sue SockJS.
Can anyone please help us out to achieve this functionality.

Trouble using res.end() in a node server

so, below is a code snippet from my server.js file. When running, and I send a URL with a message, the res.end() causes the view to render a blank page.
When I comment out the res.end() command, the view displays all of the messages, but the browser waits and waits for the signal that the response from the server is complete.
I get that you can use res.end() and put data in the parens, to be transmitted and rendered by the view.
What I expect to happen is that with no args, it will just leave the view alone, but the empty args in the parens is manifesting as an empty view.
How do I indicate that the response is complete without deleting the data on the view?
server.js
var http = require('http'),
url = require('url'),
fs = require('fs');
var messages = ["testing"];
var clients = [];
http.createServer(function(req,res) {
var url_parts = url.parse(req.url);
console.log(url_parts);
if(url_parts.pathname == '/') {
// file serving
fs.readFile('./index.html', function(err, data) {
// console.log(data);
res.end(data);
});
} else if(url_parts.pathname.substr(0,5) == '/poll'){
//polling code
var count = url_parts.pathname.replace(/[^0-9]*/,'');
console.log(count);
if(messages.length > count){
res.end(JSON.stringify({
count: messages.length,
append: messages.slice(count).join("\n")+"\n"
}));
} else {
clients.push(res);
}
} else if(url_parts.pathname.substr(0, 5) == '/msg/') {
// message receiving
var msg = unescape(url_parts.pathname.substr(5));
messages.push(msg);
while(clients.length > 0) {
var client = clients.pop();
client.end(JSON.stringify({
count: messages.length,
append: msg+"\n"
}));
}
// res.end(); //if left in, this renders an empty page, if removed,
// client keeps waiting....
}
}).listen(8080, 'localhost');
console.log('server running!');
index.html
<html>
<head>
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script>
var counter = 0;
var poll = function() {
$.getJSON('/poll/'+counter, function(response) {
counter = response.count;
var elem = $('#output');
elem.text(elem.text() + response.append);
//elem.text(counter);
poll();
});
}
poll();
</script>
</head>
<body>
<textarea id="output" style="width: 90%; height: 90%;">
</textarea>
</body>
</html>
I have looked in the docs, but I don't see anything specific about using .end() method with empty args to signify and end without passing data to be rendered. I have googled this, but I don't have an answer yet.
Do a res.json({success:"true"}) instead. The reason being is because res.end inherently thinks the client was sent a view prior to the stream being closed. With res.json() you can send any generic data, without an implied view being expected as well as close out the stream on client and server side.
Move res.end() inside while loop
while (clients.length > 0) {
var client = clients.pop();
client.end(JSON.stringify({
count : messages.length,
append : msg + "\n"
}));
if(!clients.length) {
res.end();
}
}
My understand of your problem is:
You have an HTML page (index.html), which has a textarea displaying all messages submitted by user. After one message is received and displayed, it will send the request for next message immediately (/poll/<n>).
To accept user's input for latest message, you open an API (/msg/<message>). When an HTTP request is sent to this API, server will extract the message, and return this message to /poll/<n> sent in step 1.
However, as HTML page (index.html) and the request to /msg/<message> happens in the same browser window, you can't let the http handler of /msg/<message> in node.js invoke res.end(), because in that case, the browser window will render the HTTP response of /msg/<message> request (blank page). Actually, you can't make the res return 200 OK, whatever data it returns. You can't make res fail the /msg/<message> request either (using req.destroy()), because in that case the browser window will render a failure/broken page, which is worse.
Unfortunately, you can't make res of /msg/<message> in node.js keep pending either. Although it will update index.html, the browser window will keep waiting...
The root cause of your problem is: browser window resource conflict between index.html and /msg/<message> response -- as long as /msg/<message> request is sent by using index.html window's URL bar, whenever its response is sent back, the window content (index.html) will be cleaned.
One solution is: using Ajax to send /msg/<message>. In this way, there would be no conflict for window resource. Example code is listed below:
<body>
<textarea id="output" style="width: 90%; height: 90%;">
</textarea>
<div>
<input type="text" id="msg">
<button type="button" onclick="submitMsg()">Submit</button>
</div>
</body>
window.submitMsg = function() {
var msg = $('#msg').val();
$.getJSON('/msg/' + msg, function(res) {
$('#msg').val('');
console.log('message sent.');
});
}
EDIT:
Another simpler solution is: open index.html in one browser window, and open /msg/<message> in another one (use res.end('message received successfully') to indicate message receiving result).

Get data back into webpage after AJAX call and serialport message.

New to JavaScript and Node.js
I have a setup where I have a raspberry pi running Node.js. The raspberry pi is connected to some embedded device through a USB to UART connection with the USB plugged into the raspberry pi. I can send and receive data at this base level just fine. The pi is connected to a router and I access it through it's IP and a browser.
I want to host a simple webpage that has a title, some text, and a button. When I click the button I want my client machine to contact the node.js server and make the pi send a message(already have a message format I am required to use) over the serial port to the embedded device. I want to wait/or not(depends on suggestions) for data to be sent back and then use that data to repopulate the text on the webpage.
What I have is close to this but not complete.
I run a 'server' on node.js off the pi. It uses express and a static page. The static page has a client side JavaScript file that executes a AJAX request when the button is clicked. On the node.js side I have express able to see the AJAX request. I then construct and send my message over serial port to the embedded device using serialport. At this point, on the Node.js side I can send back a string of text/etc. that can be displayed by the webpage but don't know how to somehow wait or other wise receive the data and send it to the webpage for displaying.
Client .html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Request Sensor Data</title>
<style type="text/css" media="screen"></style>
</head>
<body>
<p>Sensor Data</p>
<p><TEXTAREA id="myTxtArea" NAME="sensorDataTxtBox" ROWS=3 COLS=30 ></TEXTAREA></br>
<button type="button" name="sensorButton" id="mySensorButton" onClick="getSensorData()" >Get Sensor Data</button></p>
<script src="clientCode.js"></script>
</body>
</html>
Client .js:
function getSensorData()
{
console.log('getSensorData() button pushed.');
var xhr = new XMLHttpRequest();
xhr.open('GET', 'sensorGET');
xhr.send(null);
xhr.onreadystatechange = function () {
var DONE = 4; // readyState 4 means the request is done.
var OK = 200; // status 200 is a successful return.
if (xhr.readyState === DONE)
{
if (xhr.status === OK)
{
//insert DOM grabs to set text in html textbox.
console.log(xhr.responseText); // 'This is the returned text.'
var textAreaDOM = document.getElementById('myTxtArea');
textAreaDOM.value = textAreaDOM.value + 'inserted sensor data here\n';
}
else
{
console.log('Error: ' + xhr.status); // An error occurred during the request.
}
}
};
}
node.js .js:
var express = require('express'),
app = express();
app.use('/', express.static(__dirname + '/'));
app.get('/sensorGET', function (req, res) {
var sensorData = getSensorData();
res.send('sensorData');
})
var serialport = require('serialport'),
portname = '/dev/ttyUSB0';
var myPort = new serialport(portname, {
baudRate: 115200,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false,
parser: serialport.parsers.byteLength(1)
});
myPort.on('open', showPortOpen);
myPort.on('data', recSerialData);
myPort.on('close', showPortClosed);
myPort.on('error', showError);
myPort.on('disconnect', showDisconnect);
function showDisconnect() {
console.log('Someone disconnected');
}
function showPortOpen()
{
console.log('port open. Data rate: ' + myPort.options.baudRate);
}
function recSerialData(data)
{
parseMessage(data);//This function is not shown but parses a message that is sent on the wire
}
function showPortClosed()
{
console.log('port closed.');
}
function showError(error)
{
console.log('Serial port error: ' + error);
}
function getSensorData()
{
myPort.write(Assume correct message is sent here);
//Can return some set text here and it will be written to the webpage.
//example: return "Temp data was asked for...";
//is there a way to wait here for the next message that comes in?
}
Probably the simplest thing will actually be to use something like socket.io and just send the data to the browser with that after every parseMessage. Because for starters if you try to make http wait for all serial data it will likely timeout, and the way things work its just easier to send every time you get a new data event from the serial port.

Websocket: onmessage is not called but only on Windows

we have a problem regarding Websocket Communication with a Windows-Client.
As minimal setup we use the python3 autobahn websocket ping-pong example.
The server is from (taken from https://github.com/crossbario/autobahn-python/blob/master/examples/asyncio/websocket/echo/server.py). The only modification is that the server sends a message to the client when the connection is opened.
The client is also taken form the autobahn pingpong example but modified in two ways. It accepts connections from a remote server and it does not send a message to the server but it expects one.
This does work well on all browsers on my Linux Machine, but it does not work from a Windows-Client. But if I send a message from the client as soon as the connection is opened, then the client is also able to receive the messages.
Here is the pyhton3 server:
from autobahn.asyncio.websocket import WebSocketServerProtocol, \
WebSocketServerFactory
class MyServerProtocol(WebSocketServerProtocol):
def onConnect(self, req.uest):
print("Client connecting: {0}".format(request.peer))
def onOpen(self):
print("WebSocket connection open.")
self.sendMessage('server hello'.encode('utf8'))
def onMessage(self, payload, isBinary):
if isBinary:
print("Binary message received: {0} bytes".format(len(payload)))
else:
print("Text message received: {0}".format(payload.decode('utf8')))
# echo back message verbatim
self.sendMessage(payload, isBinary)
def onClose(self, wasClean, code, reason):
print("WebSocket connection closed: {0}".format(reason))
if __name__ == '__main__':
import asyncio
factory = WebSocketServerFactory(u"ws://0.0.0.0:9000", debug=False)
factory.protocol = MyServerProtocol
loop = asyncio.get_event_loop()
coro = loop.create_server(factory, '0.0.0.0', 9000)
server = loop.run_until_complete(coro)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
server.close()
loop.close()
Here is the Websocket Client:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var socket = null;
var isopen = false;
window.onload = function() {
socket = new WebSocket("ws://" + location.hostname + ":9000");
socket.onopen = function() {
console.log("Connected!");
isopen = true;
//if I do this, then it works
//socket.send('hello from client'.encode('utf-8'))
}
socket.onmessage = function(e) {
console.log("Text message received: " + e.data);
}
socket.onclose = function(e) {
console.log("Connection closed.");
socket = null;
isopen = false;
}
};
</script>
</head>
<body>
</body>
</html>
Has anybody an idea what I am missing? I want to open a connection from server to client without sending a message from the client first.

Javascript not working when accessing from remote computer

I accessed my client page using this link
http://XX.XX.XX.XX/project/client.php
.It has few lines of javascript and html but its not working at all . But when i access my client page using this link
http://localhost/project/client.php
, it works. I know something i should change in javascript code but i dont know what . So please tell me . Here is my client code :
<html>
<head>
<style>
#chatlog {width:440px; height:200px; border:1px solid;overflow:auto;}
#userslog {width:440px; height:200px; border:1px solid;overflow:auto;}
#msg {width:330px; height:100px;}
</style>
<script>
function initialize(){
var host = "ws://localhost:12345/project/server3z.php";
try{
socket = new WebSocket(host);
chatlog('WebSocket - status '+socket.readyState);
socket.onopen = function(event){chatlog("WebSocket status "+this.readyState); };
socket.onmessage = function(event){ chatlog(event.data); };
socket.onclose = function(){ chatlog("WebSocket status "+this.readyState); };
socket.onerror = function(event){chatlog("Error :"+event.data); };
}
catch(err){ chatlog(err); }
}
function send()
{
var chat;
chat= document.getElementById("msg").value;
if(!chat){ alert("Message can not be empty"); return; }
try{ socket.send(chat); chatlog('Sent: '+chat); } catch(err){ log(err); }
document.getElementById("msg").value = "";
}
function quit(){
chatlog("closed!");
socket.close();
chatlog("WebSocket status "+socket.readyState);
}
function chatlog(msg)
{
var match=msg.match(/10101010101010/g);
if(match)
{
var msg=msg.split("10101010101010");
document.getElementById("userslog").innerHTML+="<br>"+msg[0];
}
else
{
document.getElementById("chatlog").innerHTML+="<br>"+msg;
}
}
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>
</head>
<body onload="initialize()">
<center>
<div id="chatlog"></div>
<input id="msg" type="textbox" onkeypress="onkey(event)"/>
<button onclick="send()">Send</button>
<button onclick="quit()">Stop</button>
<div id="userslog"></div>
</center>
</body>
</html>
Don't hard-code the host to localhost, use location.hostname instead:
var host = "ws://" + location.hostname + ":12345/project/server3z.php";
You are using WebSocket are you sure that your browser in the other PC it's supports HTML5 and WebsoCKET?
WebSocket was introduced early
this line i sthe problem var host = "ws://localhost:12345/project/server3z.php";
localhost by default means your local machine. so when you access it from you local machine it maps to the correct machine but when you access it from a remote server it just searches that server because now the localhost is changed
In your code you have a hardcoded reference to a url on your local host:
var host = "ws://localhost:12345/project/server3z.php";
If you want to access it from another computer, you'll need to replace that with a domain or ip address that the remote client can resolve.
thanks for answer !! :)
madthew was right . I was using IEwhich does not support websocket. It is working in my mozila now.

Categories