Use net.server to handle http and express all at once - node.js

i have a server using net.Server and also have 2 servers using http.Server and express(). and it looks like this
...
var s_net = net.createServer();
var s_http = http.createServer();
var s_exp = express();
s_net.listen(opt.port);
s_http.listen(s_net);
s_exp.listen(s_net);
console.log(`listening on ${opt.port}`);
I use s_net as a handler for s_http and s_exp. and then, i do
s_net.on("connection", function(c){
console.log("incoming connection to s_net")
})
s_http.on("request", function(req, res){
console.log("request to s_http");
});
s_exp.all("/*", function(req, res, next){
console.log("request to s_exp");
res.send("<h1>Hello World!</h1>");
});
but on console, i just get
H:\NodeJS\web3>node index.js
listening on 80
request to s_exp
where does, it only respond from s_exp request.
How can I got them all?
incoming connection to s_net, request to s_http, and request to s_exp
references :
https://nodejs.org/dist/latest-v9.x/docs/api/net.html#net_server_listen_handle_backlog_callback

When you create a server using Express, you already have an http server. It's just an http server that has Express as a request handler for incoming requests. And, an http server inherits from net.Server so it's already a TCP/IP server. Create the one express server and then you can use any express, http or net.Server features on it including setting event listeners for various server events.
So, just creating a generic Express server already has all three together. There is no need or point to creating three separate servers and then trying to combine them somehow.

Related

Tweak nodejs socket.io behavior when receving a connection upgrade request

I am using socket.io with nodejs to start a nodejs server with websocket capabilities.
Roughly the code looks like this.
var fs = require('fs'),
app = require('http').createServer(handler),
io = require('socket.io')(app);
app.listen(8999);
function handler (req, res) {
console.log("http");
res.writeHead(101);
res.end();
}
io.on('connection', function (socket) {
socket.emit("A new connection is established");
});
One problem I have, is that when the client requests GET http://localhost:8999/ with header
Connection: Upgrade
Upgrade: websocket
I noticed that the server will not use the web server handler, and instead it does something else.
How do I ensure that when it receives any http request, the http handler is invoked? Due to the inflexibility of my client, I actually do need it to return a HTTP 101 Switching Protocols for that particular request - even with those special headers.

Socket.io-based app running through node proxy server disconnecting all sockets whenever one disconnects

I made a basic chat app using node.js, express and socket.io. It's not too different from the tutorial chat app for socket.io, it simply emits events between connected clients. When I ran it on port 3001 on my server, it worked fine.
Then I made a proxy server app using node-http-proxy which listens on port 80 and redirects traffic based on the requested url to various independent node apps I have running on different ports. Pretty straightforward. But something is breaking. Whenever anyone disconnects, every single socket dis- and re-connects. This is bad for my chat app, which has connection-based events. The client consoles all show:
WebSocket connection to 'ws://[some socket info]' failed: Connection closed before receiving a handshake response
Here's what I think are the important parts of my code.
proxy-server.js
var http = require('http');
var httpProxy = require('http-proxy');
//create proxy template object with websockets enabled
var proxy = httpProxy.createProxyServer({ws: true});
//check the header on request and return the appropriate port to proxy to
function sites (req) {
//webapps get their own dedicated port
if (req == 'mychatwebsite.com') {return 'http://localhost:3001';}
else if (req == 'someothersite.com') {return 'http://localhost:3002';}
//static sites are handled by a vhost server on port 3000
else {return 'http://localhost:3000';}
}
//create node server on port 80 and proxy to ports accordingly
http.createServer(function (req, res) {
proxy.web(req, res, { target: sites(req.headers.host) });
}).listen(80);
chat-app.js
/*
...other modules
*/
var express = require("express");
var app = exports.app = express(); //I probably don't need "exports.app" anymore
var http = require("http").Server(app);
var io = require("socket.io")(http);
io.on("connection", function (socket) {
/*
...fun socket.on and io.emit stuff
*/
socket.on("disconnect", function () {
//say bye
});
});
http.listen(3001, function () {
console.log("listening on port 3001");
});
Now from what I've read on socket.io's site, I might need to use something to carry the socket traffic through my proxy server. I thought that node-http-proxy did that for me with the {ws: true} option as it states in their docs, but apparently it doesn't work like I thought it would. socket.io mentions three different things:
sticky session based on node's built in cluster module
socket.io-redis, which allows separate socket.io instances to talk to each other
socket.io-emitter, which allows socket.io to talk to non-socket.io processes
I have exactly no idea what any of this means or does. I am accidentally coding way above my skill level here, and I have no idea which of these tools will solve my problem (if any) or even what the cause of my problem really is.
Obligatory apology: I'm new to node.js, so please forgive me.
Also obligatory: I know other apps like nginx can solve a lot of my issues, but my goal is to learn and understand how to use this set of tools before I go picking up new ones. And, the less apps I use, the better.
I think your intuition about needing to "carry the socket traffic through" the proxy server is right on. To establish a websocket, the client makes an HTTP request with a special Upgrade header, signalling the server to switch protocols (RFC 6455). In node, http.Server instances emit an upgrade event when this happens and if the event is not handled, the connection is immediately closed.
You need to listen for the upgrade event on your http server and handle it:
var proxy = httpProxy.createProxyServer({ws: true})
var http = http.createServer(/* snip */).listen(80)
// handle upgrade events by proxying websockets
// something like this
http.on('upgrade', function (req, socket, head) {
proxy.ws(req, socket, head, {target:sites(req.headers.host)})
})
See the node docs on the upgrade event and the node-http-proxy docs for more.

support multiple protocols on single server

I have a working Express HTTP server as well as a working websocket server. I want to add the websockets application to my regular website which is run by the HTTP server, but I'm not sure I'm understanding the documentation. Can I have a server that accepts multiple protocols and how would I handle the routing in a situation like that? The npmjs documentation for socketio says:
In conjunction with Express
Starting with 3.0, express applications have become request handler functions that you pass to http or http Server instances. You need to pass the Server to socket.io, and not the express application function.
var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
io.on('connection', function(){ /* … */ });
server.listen(3000);
can I handle HTTP requests through app.HTTPverbHere() and websocket requests through io.on?
The socket.io documentation shows you the exact steps needed to make socket.io work with nodejs express on the same server.
So, YES, you can do this.
In fact, every webSocket connection starts with an HTTP request (which is then upgraded to the webSocket protocol) so you must have a web server running on the server that handles webSockets anyway.
socket.io simply hooks into one route on the express web server that is used to initiate all socket.io webSocket connections and handles things from there.
Here's one example taken directly from the socket.io doc:
var app = require('express').createServer();
var io = require('socket.io')(app);
app.listen(80);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});

Need for http.createServer(app) in node.js / express

Using node and express, the below works just fine.
var app = express();
app.listen(app.get('port'), function() {
});
I assume that a server is created implicitly in the above construct.
When adding socket.io, I've seen the following being done.
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
app.listen(app.get('port'), function() {
});
What is the need for explicitly adding http.createServer(app) ? Won't the creation of an additional server mess up things ? Or put it other way, is it ok to create many more http.createServer(app) ?
In either case, only one server is created. When using socket.io, you share the same http server between socket.io and express. Both libraries attach event listeners to the same server and have a chance to respond to the same events. They cooperate nicely because socket.io only handles relevant requests and express handles all the non-websocket requests. And just FYI you could not create more than one server on the same port. Only one process can listen on a TCP port at a time in the OS, so the second one would fail with an error when attempting to bind an in-use port.

Listening for HTTP traffic on master process, handling connections on child process?

I'm looking for a way to have a socket listening for HTTP requests on a master process, then passing any incoming connections and the request data to a child process using process.send() and handling the HTTP request in the child process.
However, I'm unable to find a way to attach the passed connection and the request data to a HTTP server in the child process, without initializing the server by .listen()ing on a socket.
So, is it somehow possible to use a HTTP server to handle a connection passed in from another process? Any non-public API solution will also do (hopefully JavaScript-side, though).
I guess I could initialize the server to listen on some dummy socket and then bounce the incoming connections off of that, but that would seem suboptimal. Passing in the listening socket as well or using Cluster or a reverse proxy would also not be optimal in my case.
It is possible, but first you need to get some understanding of how node.js works:
A TCP server (from net.createServer) is basically an EventEmitter which only emits connection events.
An HTTP server (from http.createServer) is a TCP server with a connection parser.
Unfortunately for you, the HTTP server is hardwired to a TCP server (see source):
function Server(requestListener) {
if (!(this instanceof Server)) return new Server(requestListener);
net.Server.call(this, { allowHalfOpen: true });
...
this.addListener('connection', connectionListener);
...
}
What you need is to pass your socket to connectionListener which is defined in http.js. The good news is that the module exports it:
exports._connectionListener = connectionListener;
I haven't tried this code, but it should work with some tweaks:
var util = require('util'),
events = require('events'),
http = require('http'),
express = require('express'),
app = express();
function FakeServer() {
events.EventEmitter.call(this);
this.on('connection', http._connectionListener);
this.on('request', app);
}
util.inherits(FakeServer, events.EventEmitter);
var server = new FakeServer();
process.on('message', function ('socket', socket) {
server.emit('connection', socket);
});
// you can use app as a regular express app
app.get('/hello', function (req, res) {
res.send('world');
};

Resources