Communication between several node.js process - sockets

I have 3 node.js application (A, B, C). A is an entry point and I like all the request that gets to this entry point to be redirected toward B or C (depending upon the value of a parameter in the request).
Currently I'm doing so with a res.redirect in A (I'm using expressjs framework). That's working fine except that it's not really transparent (I can see from the outside that the original request has been redirected).
To solve this, I will have B and C listen on socket instead of port number but I do not know how to have A redirecting the request to the sockets used by B or C.
Any idea on how to have 2 node.js process communicating via sockets ?
** UPDATE **
I have changed the code of A to use node-proxy:
app.all('/port/path/*', function(req, res){
// Host to proxied to
var host = 'localhost';
// Retrieve port and build new URL
var arr_url = req.url.split('/');
var port = arr_url[1];
var new_url = '/' + arr_url.slice(2).join('/');
console.log("CURRENT URL:" + req.url); // url is /5000/test/...
console.log("NEW URL :" + new_url); // url is /test/...
// Change URL
req.url = new_url;
var proxy = new httpProxy.HttpProxy();
proxy.proxyRequest(req, res, {
host: host,
port: port,
enableXForwarded: false,
buffer: proxy.buffer(req)
});
console.log("Proxied to " + host + ':' + port + new_url);
// For example: the url is localhost:10000/5000/test
// the log tells me 'Proxied to localhost:5000/test' => that is correct
// But... I do not get any return
// If I issue 'curl -XGET http://localhost:5000/test' I get the return I expect
});
Any obvious mistake in this ?

You're on the right track with having the other processes listen on different ports. What you're talking about is called a reverse proxy. If its http, its pretty straightforward to get this going with node-http-proxy:
https://github.com/nodejitsu/node-http-proxy
You want to set something up like a proxy table:
var options = {
router: {
'foo.com/baz': '127.0.0.1:8001',
'foo.com/buz': '127.0.0.1:8002',
'bar.com/buz': '127.0.0.1:8003'
}
};

I just put in an issue for for the node-http-rpoxy module here https://github.com/nodejitsu/node-http-proxy/issues/104
Apparently it does in fact work with unix sockets right now, but like this (subject to change).
var options = {
router: {
'foo.com/baz': ':/tmp/nodeserver.sock',
}
};
All it needs is a colon to use the socket path as the port value.

Related

Suave with f# - How to have a rest api and websocket port in an f# chat application?

I have an f# chat application that needs rest apis exposed as well as have websockets for real time messaging. I am using Suave framework.
I have a frontend which has a "Chat" button that runs javascript on click. The javascript triggers the creates a web socket for the websocket url (/websocket) and establishes a connection for the real time chats. However, I have another button "FetchUsers" for fetching all the users in the system. But for this, I will need a different path (/people). I dont think I can use the (/websocket) path because in the backend, there is only one function which can be defined for a particular path.
To achieve this I have come up with the idea that I could expose 2 different ports : one for the rest api and one for the web sockets. My rest api (localhost:8082/people) is used to get all the users in the system and the web sockets (localhost:8080/websocket) is for sending chat messages to all users in the system
I am open to any other suggestions as to how can I implement the chat application.
Program.fs
let app : WebPart =
choose [
path "/websocket" >=> handShake ws
path "/websocketWithSubprotocol" >=> handShakeWithSubprotocol (chooseSubprotocol "test") ws
path "/websocketWithError" >=> handShake wsWithErrorHandling
GET >=> choose [ path "/" >=> file "index.html"; browseHome ]
NOT_FOUND "Found no handlers." ]
let myCfg =
{ defaultConfig with
bindings = [
HttpBinding.createSimple HTTP "127.0.0.1" 8080
HttpBinding.createSimple HTTP "127.0.0.1" 8082
]
}
[<EntryPoint>]
let main _ =
let personWebPart = rest "people" {
GetAll = Db.getPeople
Create = Db.createPerson
}
startWebServer myCfg personWebPart
startWebServer myCfg app
Restful.fs
module RestFul =
open Suave.Web
open Suave.Successful
open Newtonsoft.Json
open Suave
open Suave.Operators
open Suave.Filters
open Suave.Files
open Suave.RequestErrors
open Newtonsoft.Json.Serialization
type RestResource<'a> = {
GetAll : unit -> 'a seq
Create : 'a -> 'a
}
let fromJson<'a> json =
JsonConvert.DeserializeObject(json, typeof<'a>) :?> 'a
let getResourceFromReq<'a> (req : HttpRequest) =
let getString (rawForm: byte[]) =
System.Text.Encoding.UTF8.GetString(rawForm)
req.rawForm |> getString |> fromJson<'a>
let JSON v =
let jsonSerializerSettings = JsonSerializerSettings()
jsonSerializerSettings.ContractResolver <- CamelCasePropertyNamesContractResolver()
JsonConvert.SerializeObject(v, jsonSerializerSettings)
|> OK
>=> Writers.setMimeType "application/json; charset=utf-8"
let rest resourceName resource =
let resourcePath = "/" + resourceName
let getAll = warbler (fun _ -> resource.GetAll () |> JSON)
path resourcePath >=> choose [
GET >=> getAll
POST >=> request (getResourceFromReq >> resource.Create >> JSON)
]
How can assign the websocket to port 8080 and rest api to port 8082? I am doing this for now. The server starts up on the ports but I am only able to access the rest url ie localhost:8080/people
startWebServer myCfg personWebPart
startWebServer myCfg app
Reiterating my previous point, I would like to know any other approaches as to how can I implement the chat application.
It has been awhile since I've used Suave but if I recall it has WebSocket support using a custom "socket" computation expression. You loop in the expression, wait for a message, and then handle it. Note that waiting on the Websocket is in itself an async operation and that each client has their own loop.
Example is on their Github:
https://github.com/SuaveIO/suave/blob/master/examples/WebSocket/Program.fs

How to connect and send file to MongoDb with angular file upload

I am trying to upload file, i am using "Angular File Upload" for it. Trying to connect and send file to mongodb but it gives this error on firebug
net::ERR_INSECURE_RESPONSE
In mongodb.log log files it shows the connection is done:
2014-11-09T11:57:05.512+0400 I NETWORK [initandlisten] connection accepted from xxx.x.x.x:53749 #2 (1 connection now open)
2014-11-09T11:57:05.524+0400 I NETWORK [conn2] end connection xxx.x.x.x:53749 (0 connections now open)
I have also create and used ssl certificate.
My Angular Js code is:
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var $file = $files[i];
$upload.upload({
url: "https://localhost:27017/mydb",
file: $file,
progress: function(e){ console.log("Progress: ");}
}).then(function(data, status, headers, config) {
// file is uploaded successfully
console.log("File Uploaded : "+ data);
});
}
};
You'll need an interim step there. Going directly from Angular to Mongo will not work out. If you want a generic REST interface to Mongo with which you can utilize Angular's bundled $http services, take a look at the list of REST services on Mongo's site.
Mongo REST Services
http://www.mongodb.org/display/DOCS/Http+Interface#HttpInterface-RESTInterfaces
Angular $http Service:
http://docs.angularjs.org/api/ng.$http
There's a lot of different options here, but this is likely the easiest way to get up and going.
var mongoose = require('mongoose');
var db = mongoose.createConnection('mongodb://localhost:3000/database');
var orderSchema = new mongoose.Schema({
routeFrom : String,
routeTo : String,
leaving: String
});
var Order = db.model('Order', orderSchema);
module.exports = Order;

Get remote endpoint properties on node.js socket close/end events

I have a node.js server which keeps track of all "clients" connected to it, using an associative array of sockets, because I need to send some data to specific clients sometimes. The keys in that array are strings composed of
socket.remoteAddress + ':' + socket.remotePort
and values are socket instances passed to the callback provided to net.createServer(...)
Sometimes the clients disconnect for one reason or another and I receive the 'end' and then 'close' events. I'd like to remove the disconnected clients from my client registry but in the close/end event callbacks, the remoteAddress and remotePort variables are undefined. Can they be retrieved somehow?
To illustrate:
var registry = {}
var server = net.createServer(function (socket) {
socket.on('connect', function(){
registry [socket.remoteAddress + ':' + socket.remotePort] = socket;
});
socket.on('close', function(had_error){
// ******************************************************
// socket.remoteAddress and remotePort are undefined here
// ******************************************************
var id = socket.remoteAddress + ':' + socket.remotePort;
// *************************************************************
if(registry.hasOwnProperty(id)
delete socket.id;
});
});
I think you should use socket.id instead of creating your own object property that might not be unique
socket.on('connect', function(){
registry [socket.id] = socket;
});
UPDATE
Node provides basic http API. Http is stateless. HTTP sessions allow associating information with individual visitors but node doesn't have a native sessions support.
Take a look at express, it is a nice web framework with sessions support
Also, if you need to send messages in a realtime fashion, take a look at socket.io
UPDATE V2
You can add your own ID property to the socket object:
function randomID(len){
//Generate a random String
return randID;
}
var registry = {}
var server = net.createServer(function (socket) {
socket.on('connect', function(){
var uid = randomID(32);
socket.id = uid;
registry [uid] = socket;
});
socket.on('close', function(had_error){
var uid = socket.id;
// *************************************************************
console.log("Socket ID " + uid + " disconnected")
});
});

How to send message to individual client in node.js using only Net module(not socket.io)

Simple code:
process.stdin.resume()
process.stdin.setEncoding('utf8');
var server = createServer();
server.listen(9999);
server.on('connection',function(sock){
console.log('CONNECTED:'+sock.remoteAddress+":"+sock.remotePort);
process.stdin.on('data',function(send){
sock.write(send);
});
}
When receiving connection from 10.10.10.1 and 10.10.10.2,
message "CONNECTED:10.10.10.1:xxx" and "CONNECTED:10.10.10.2:xxx"
are display on terminal
To send message to a client, I used sock.write(send)..
but, All clients received message
How can I send a message to a specific client.
From googling there are many socket.io related documents(solutions)..
but, before using socket.io, I want to know node.js itself.
(or javascript itself?)
After reading Vadim's comment, I wrote down more code below.
fully working code.
I add two things. According to Vadim's comment, add property sock.id
and using property sock.remoteAddress, send server's stdin message to
10.10.10.1 client only
var net = require('net')
process.stdin.resume()
process.stdin.setEncoding('utf8');
var server = net.createServer();
server.listen(9999);
server.on('connection',function(sock){
sock.write('input your ID: ',function(){
var setsockid = function(data){
id=data.toString().replace('\r\n','');
console.log('ID:'+id+' added!!')
sock.id=id
sock.removeListener('data',setsockid);
};
sock.on('data',setsockid);
sock.on('data',function(data){
d=data.toString().replace('\r\n','');
console.log(sock.id+' say: '+d);
});
});
console.log('CONNECTED:'+sock.remoteAddress+":"+sock.remotePort);
process.stdin.on('data',function(send){
if (sock.remoteAddress=='10.10.10.1') sock.write(send);
});
});
Answer to your question is on Node.JS main page.
var net = require('net');
var server = net.createServer(function (socket) {
socket.write('Echo server\r\n');
socket.pipe(socket);
});
server.listen(1337, '127.0.0.1');

Node.js https.request with keep-alive header

I want to pull posts from a users Facebook wall.
The following code snippet works, but it never terminates:
var https = require('https');
facebookWall = function(user) {
var options = {
host: 'graph.facebook.com',
port: 443,
path: '/me/home?access_token=' + user.facebook_token + '&since=' + encodeURIComponent(user.facebook_timestamp),
method: 'GET',
headers: {
'Connection':'keep-alive'
}
};
var req = https.request(options)
.on('response', function(response) {
var body = '';
response.on('data', function(data) {
body += data;
try {
var wallPosts = JSON.parse(body);
console.log("user " + user.id + " has " + wallPosts.data.length + " new items on their wall");
}
catch (e) {
//console.log("waiting for more data chunks...");
}
})
});
req.end();
req.on('error', function(e) {
console.error(e);
});
}
I think it is caused by the 'Connection':'keep-alive' header. When I replace it with 'Connection':'close' the script will terminate when all data has been retrieved from facebook.
I'm hoping to be able to use the keep-alive header to prevent having to create a new SSL connection for each request. I have thousands of requests and with the keep-alive header, it completes in just a few seconds, as opposed to a few minutes without the keep-alive header.
Does anyone know how to accomplish this? I'm fairly new to Node.JS, so if I'm missing something obvious, I apologize.
It's because keep-alive is not yet implemented for https/tls/ssl
in node 4.x and I believe for 6.x too. That's why in node websocket-server
it doesn't work as well, see https://github.com/nephics/node-websocket-server/commit/3a732bff6aabe694834d87086a7718be7c0ce138
I notice you're using https. Depends on what version of node you're using, but there is a known issue with the end event right now.
https://github.com/joyent/node/issues/728
You have to do the following:
1) Put response.on('end', function() { ... do the output ... }); for the https.request
Don't output data in the response.on('data', ...);
2) Use Connection:keep-alive , "close" will cause very poor performance issue. I have done lot of testing and I can confirm this.
Other than that:
3) In your options , set the agent, and set agent.maxSockets to a larger number if you need concurrency. default is only 5.
4) You should consider to make your own routine to handle https.request timeout. (please go to github/joyent and search for it. basically use setTimeout to emit a timeout error).