How can I create a Twitter stream using Node.js and Websockets? - node.js

A few months ago (August 2011) I successfully created a node.js websockets server which connected to Twitter's Streaming API using basic HTTP user/password authentication. To do this, I employed Andre Goncalves' twitter-nodejs-websocket library.
Since creating this working implementation, Twitter has eliminated access to the streaming API via basic HTTP auth, in favor of OAuth. After this shift, I utilized Ciaran Jessup's node-oauth library, which has successfully given me access to the Streaming API again (when I run the server I am successfully outputting the tweets via console.log(tweet) -- see below ).
The problem now is that my websockets server is no longer working. When I run my server from the command line and hit the client web page from the browser, the websocket "onclose" event is immediately fired.
I've tried everything I can think of to get this working. Any help would be very greatly appreciated!
server.js
var sys = require('sys'),
http = require('http'),
ws = require("./vendor/ws"),
base64 = require('./vendor/base64'),
arrays = require('./vendor/arrays')
var OAuth = require('./oauth/oauth').OAuth;
var consumer_key = '[...]'; //removed for obvious security reasons...
var consumer_secret = '[...]';
var access_token = '[...]';
var access_token_secret = '[...]';
oa = new OAuth("https://twitter.com/oauth/request_token",
"https://twitter.com/oauth/access_token",
consumer_key,
consumer_secret,
"1.0A",
null,
"HMAC-SHA1");
var request = oa.get("https://stream.twitter.com/1/statuses/filter.json?track=google", access_token, access_token_secret );
// Response Parsing -------------------------------------------- //
var clients = [];
var message = "";
request.addListener('response', function (response) {
response.setEncoding('utf8');
response.addListener("data", function (chunk) {
message += chunk;
var newlineIndex = message.indexOf('\r');
// response should not be sent until message includes '\r'.
// Look at the section titled "Parsing Responses" in Twitter's documentation.
if (newlineIndex !== -1) {
var tweet = message.slice(0, newlineIndex);
clients.forEach(function(client){
// Send response to all connected clients
client.write(tweet);
});
// this just tests if we are receiving tweets -- we are: terminal successfully outputs stream //
var pt = JSON.parse(tweet);
console.log('tweet: ' + pt.text);
}
message = message.slice(newlineIndex + 1);
});
});
request.end();
// Websocket TCP server
ws.createServer(function(websocket){
clients.push(websocket);
websocket.addListener("connect", function(resource){
// emitted after handshake
sys.debug("connect: " + resource);
}).addListener("close", function(){
// emitted when server or client closes connection
clients.remove(websocket);
sys.debug("close");
});
}).listen(8081);
// This basic http server works, so we know this port is open.
//
// var http = require('http');
// http.createServer(function (req, res) {
// res.writeHead(200, {'Content-Type': 'text/plain'});
// res.end('Hello World\n');
// }).listen(8081);
client code
<script type="text/javascript" charset="utf-8">
ws = new WebSocket("ws://ec2-67-202-6-10.compute-1.amazonaws.com:8081");
ws.onmessage = function(evt) {
console.log('tweet')
};
ws.onclose = function() {
console.log("socket closed");
};
ws.onopen = function() {
console.log("connected...");
};
</script>

Maybe you updated the browser? The websocket spec is chaning rapidly. Anyway, I'd propose using socket.io because it will even still work with fallbacks if the browser is outdated or websockets got incompatible again or a crappy proxy is preventing websockets from working.

Have a look at this sample event stream (it uses server sent events) from a twitter stream:
https://github.com/chovy/nodejs-stream

Related

What are the minimum requirements to get a secure WebSocket working with Node.js?

When it comes to WebSockets, it seems the internet has plenty of ws:// (insecure) tutorials, but hardly any on wss:// (secure). I've got an insecure websocket connection working fine with Node.js thanks to Supun Kavinda's tutorial, but can't get it to work as a secure connection.
Here's the summarized working code for the insecure connection (fake ip address):
File: js-index.js
window.WebSocket = window.WebSocket || window.MozWebSocket;
var connection = new WebSocket('ws://3.00.00.00:8080');
File: server.js
const https = require('https');
const fs = require('fs');
var WebsocketServer = require('websocket').server;
var server = https.createServer(function(request,response) {
function getPostParams(request, callback) {
var qs = require('querystring');
if (request.method == 'POST') {
var body = '';
request.on('data', function (data) {
body += data;
// Too much POST data, kill the connection!
if (body.length > 1e6)
request.connection.destroy();
});
request.on('end', function () {
var POST = qs.parse(body);
callback(POST);
});
}
}
if (request.method === "POST") {
getPostParams(request, function(POST) {
messageClients(POST.data);
response.writeHead(200);
response.end();
});
return;
}
});
server.listen(8080);
/*Handling websocket requests*/
var websocketServer = new WebsocketServer({
httpServer: server
});
websocketServer.on("request", websocketRequest);
global.clients = {}; // connected clients
var connectionId = 0;
function websocketRequest(request) {
var connection = request.accept(null, request.origin);
connectionId++;
clients[connectionId] = connection;
}
function messageClients(message) {
for (var i in clients) {
clients[i].sendUTF(message);
}
}
For a secure connection, I have tried many things, mainly centered on changing ws:// to wss://, and including a .pem certificate/key in the createServer() call, like so:
const ssl_creds = {
cert: fs.readFileSync('/opt/bitnami/apache2/htdocs/fullchain.pem'),
key: fs.readFileSync('/opt/bitnami/apache2/htdocs/privkey.pem')
};
var server = https.createServer(ssl_creds, function(request,response) {....
However, I continue to get errors (only testing in Chrome thus far), such as:
Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID
Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
^#1 being the problem I encounter most
I'm using the same ssl credentials granted by LetsEncrypt for my website itself - I assume that is okay?
In summary, most of the tutorials I've found either do something exactly like this, or talk about how something like NGINX is required to setup proxies/tunnels - something I am completely clueless on.
Can someone tell me if proxy configurations are typically required for this, or if I'm just missing something small/dumb? Any insight at all would be appreciated. In case it's relevant, I'm just cutting my teeth on AWS with a Lightsail PHP image from Bitnami.

node.js - issue with faye-websocket-node as client and sockjs-node as server

I'm trying to use faye-websocket-node ^0.11.0 as client, sockjs-node 0.3.18 as server, please refer to below client/server code
Issue:
if client and server prefix matchs, like below code prefix = /hello, client will throw Error during WebSocket handshake: Unexpected response code: 200
I tried using prefix = / on the server, this time there won't be any error message, but there's no connection open, no message printed from either client or server on open or on connection code
Question:
How to use faye-websocket-node as client, sockjs-node as server, and have them communicate via websock using node.js?
Thanks!
client code:
var WebSocket = require('faye-websocket'),
ws = new WebSocket.Client('ws://127.0.0.1:8888/hello');
ws.on('open', function(event) {
console.log('open');
ws.send('Hello, world!');
});
ws.on('message', function(event) {
console.log('message', event.data);
});
ws.on('close', function(event) {
console.log('close', event.code, event.reason);
ws = null;
});
ws.on('error', function(event){
console.log('error', event.message);
});
server code:
var sockjs = require('sockjs');
var http = require('http');
var sockjsServer = sockjs.createServer({
sockjs_url: '//d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.min.js'
});
sockjsServer.on('connection', function(conn) {
console.log('on connection');
conn.on('data', function(msg) {
console.log('\nRECV:'+msg);
});
});
var server = http.createServer();
sockjsServer.installHandlers(server, {prefix:'/hello'});
server.listen(8888, '0.0.0.0');
I raised ticket#229, answer from sockjs-node:
You cannot use faye-websocket directly as SockJS utilizes additional framing. You can do one of two things:
1. Use the "raw" endpoint as documented in the README: https://github.com/sockjs/sockjs-node#connecting-to-sockjs-node-without-the-client
2. Use sockjs-client from Node.js! Client versions >= 1.0 support usage directly from Node.

Failed to load resource: the server responded with a status of 426 (Upgrade Required)

I tried to go with the tutorial of this link http://web-engineering.info/node/57
But when I execute node server.js and open the browser http://localhost:3434 it says upgrade required. The server.js file is:
var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({port: 3434});
wss.broadcast = function (data) {
var i = 0, n = this.clients ? this.clients.length : 0, client = null;
for (; i < n; i++) {
client = this.clients[i];
if (client.readyState === client.OPEN) {
client.send(data);
}
else console.error('Error: the client state is ' + client.readyState);
}
};
wss.on('connection', function (ws) {
ws.on('message', function (message) {
wss.broadcast(message);
});
});
you have to open your index.html in browser not http://127.0.0.1:3434
its a websocket server. You are trying to make a http connection to a websocket server.
Most probably your server socket at localhost:3434 don't have support for websocket, so the connection is terminated by the client browser.
This error indicates that on localhost:3434 you are running a HTTP server which is incapable to "upgrade" to websocket.
(Since both simple http and websocket begins with a simple http request. In that http request the client ask the server to switch to websocket protocol.)
Should you add this ?
var ws = require('websocket.io')
, server = new ws.Server()
// … somewhere in your http server code
server.on('upgrade', function (req, socket, head) {
server.handleUpgrade(req, socket, head);
});
ref https://www.npmjs.com/package/websocket.io#passing-in-requests
Check this SO too What is an http upgrade?
I tried intercepting the http request
var ws = require('websocket.io')
, http = require('http').createServer().listen(3000)
, server = ws.attach(http)
server.on('connection', function (socket) {
socket.on('message', function () { });
socket.on('close', function () { });
});
https://www.npmjs.com/package/websocket.io#passing-in-requests
For me using the npmjs documentation, I went copy-paste rogue.
Then, debugging my client-side request I noted that the URL parameter I was using was not a string as expected.
But it was shark_s's answer that helped remind me to go look at the console and interpret the error again- so thanks.

how to flush the buffer in TCP connection

Hi I am facing issue with a server client message passing from web client to TCP server . Every time I reconnect to the web page my first 6 messages passes with out delay and the seventh message takes lots of time and the first message repeats again. I think there should be some handler for buffer but I have no idea of how to start it. Help me to solve this. My server and client both are in node socket ( using var net = require('net') ).
my client has to send a response for the ajax call which i made from web page:
$.ajax({
type: 'POST',
url: 'http://localhost:3000/client',
dataType: "json",
contentType: "application/json; charset=UTF-8",
data: JSON.stringify({name:data+'\r'}),// this is the data i get from web page
done : function(data){
console.log('on success', data);
},
fail : function(error){
console.log('on error', error)
}
})
and my node client
var net = require('net');
var _ = require('lodash');
router.post('/client', function(req, res) {
var inputJSON = req.body;
var HOST = '127.0.0.1';
var PORT = 5000;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
// Write a message to the socket as soon as the client is connected, the server will receive it as message from the client
_.forEach(inputJSON, function(value,key){
client.write(value);
// console.log(value);
})
});
//This is the line i missed in my earlier program the client should respond
res.send('success')
});
I am learning node. so you can improvise my code with your comments so i can improve better thanks.

connect cmd line socket server via nodejs socket.io

I have a node.js server communicating to a client web page, sending it message. This is working great based on the many tutorials and searching stack overflow :)
Where I am having an issue is when I attempt to startup a separate socket connection to a 3rd party cmd line executable instance runs as a socket server. The 3rd party executable does not adhere to the socket.io namespace/room type of events, so I read that socket.io-events may help where instead of:
socket.on('some key', function(){/**do stuff*/}); I could:
eventRouter.on('*', function(){/*do stuff*/});
For this communication, I am assuming I need to use socket.io-client on the node.js side in order to talk to the cmd executable, but I am getting exceptions trying to do a socket2.use(router); where socket2 is my socket.io-client and router is the socket.io-events object.
All runs on localhost, node to web page is port 8001 and node.js to executable is on port 8002. Please pardon the code, for I have been trying to get this to work for a few days and is a bit ugly now.
The cmd executable to execute and its arguments I have coming from the web page which works. I am able to start the exe. The EXE expects a ACK on each message sent, thus why you see the code emitting it back.
I have a interval where I set and update an element on the web page. I have another element that I set messages (msg).
var http = require('http');
var url = require('url');
var fs = require('fs');
var server;
server = http.createServer(function(req, res){
// your normal server code
var path = url.parse(req.url).pathname;
switch (path){
case '/':
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Hello! Try the Test page </h1>');
res.end();
break;
case '/socket.html':
fs.readFile(__dirname + path, function(err, data){
if (err){
return send404(res);
}
res.writeHead(200, {'Content-Type': path == 'json.js' ? 'text/javascript' : 'text/html'});
res.write(data, 'utf8');
res.end();
});
break;
default: send404(res);
}
}),
send404 = function(res){
res.writeHead(404);
res.write('404');
res.end();
};
server.listen(8001);
var str = "ack0";
var bytes = [];
for (var i = 0; i < str.length; ++i) {
bytes.push(str.charCodeAt(i));
}
// use socket.io
var io = require('socket.io').listen(server);
// define interactions with client
io.sockets.on('connection', function(socket){
//send data to client
setInterval(function(){
socket.emit('date', {'date': new Date()});
}, 1000);
//recieve client data
socket.on('client_data', function(data){
var spawn = require('child_process').spawn;
console.log('pre-spawned');
spawn(data.cmd, data.args, {});
setTimeout(function() {
console.log('hello world!');
}, 1000);
var aptIO = require('socket.io-client');
var router = require('socket.io-events')();
var socket2 = aptIO.connect('localhost:8002', {reconnect: true});
router.on('connection', function(s){
//send data to client
console.log('apt');
router.on('*', function(sock, args, next){
var name = args.shift(), msg = args.shift();
console.log(name + " " + JSON.stringify(msg));
sock.emit(bytes);
io.sockets.emit('msg', {'msg': JSON.stringify(msg)})
next();
});
s.emit(bytes);
});
console.log('spawned');
// getting runtime exceptions here...have tried various things...
socket2.use(router);
});
});
With the help from JGreenwell, I was able to resolve me issue.
I ended up having the node server communicate to the client html page via socket.io connection for messages. The node server would launch the cmd line executable providing it the port to connect to which is different from the socket.io port used.
Once started, the executable would communicate with the server via the net module. The server would just pass the information on to the socket.io connection. the js in the html page knows how to parse the message in order to increment the progress bar and list the messages in a text area control.
I took it even further by having the messages be broadcast-ed to multiple clients on the socket.io connection.

Resources