I'm trying to get socket.io running with my SSL certificate however, it will not connect.
I based my code off the chat example:
var https = require('https');
var fs = require('fs');
/**
* Bootstrap app.
*/
var sys = require('sys')
require.paths.unshift(__dirname + '/../../lib/');
/**
* Module dependencies.
*/
var express = require('express')
, stylus = require('stylus')
, nib = require('nib')
, sio = require('socket.io');
/**
* App.
*/
var privateKey = fs.readFileSync('../key').toString();
var certificate = fs.readFileSync('../crt').toString();
var ca = fs.readFileSync('../intermediate.crt').toString();
var app = express.createServer({key:privateKey,cert:certificate,ca:ca });
/**
* App configuration.
*/
...
/**
* App routes.
*/
app.get('/', function (req, res) {
res.render('index', { layout: false });
});
/**
* App listen.
*/
app.listen(443, function () {
var addr = app.address();
console.log(' app listening on http://' + addr.address + ':' + addr.port);
});
/**
* Socket.IO server (single process only)
*/
var io = sio.listen(app,{key:privateKey,cert:certificate,ca:ca});
...
If I remove the SSL code it runs fine, however with it I get a request to http://domain.com/socket.io/1/?t=1309967919512
Note it's not trying https, which causes it to fail.
I'm testing on chrome, since it is the target browser for this application.
I apologize if this is a simple question, I'm a node/socket.io newbie.
Thanks!
On the same note, if your server supports both http
and https
you can connect using:
var socket = io.connect('//localhost');
to auto detect the browser scheme and connect using http/https accordingly. when in https, the transport will be secured by default, as connecting using
var socket = io.connect('https://localhost');
will use secure web sockets - wss://
(the {secure: true}
is redundant).
for more information on how to serve both http and https easily using the same node server check out this answer.
Server-side:
import http from 'http';
import https from 'https';
import SocketIO, { Socket } from 'socket.io';
import fs from 'fs';
import path from 'path';
import { logger } from '../../utils';
const port: number = 3001;
const server: https.Server = https.createServer(
{
cert: fs.readFileSync(path.resolve(__dirname, '../../../ssl/cert.pem')),
key: fs.readFileSync(path.resolve(__dirname, '../../../ssl/key.pem'))
},
(req: http.IncomingMessage, res: http.ServerResponse) => {
logger.info(`request.url: ${req.url}`);
let filePath = '.' + req.url;
if (filePath === './') {
filePath = path.resolve(__dirname, './index.html');
}
const extname = String(path.extname(filePath)).toLowerCase();
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.json': 'application/json'
};
const contentType = mimeTypes[extname] || 'application/octet-stream';
fs.readFile(filePath, (error: NodeJS.ErrnoException, content: Buffer) => {
if (error) {
res.writeHead(500);
return res.end(error.message);
}
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
});
}
);
const io: SocketIO.Server = SocketIO(server);
io.on('connection', (socket: Socket) => {
socket.emit('news', { hello: 'world' });
socket.on('updateTemplate', data => {
logger.info(data);
socket.emit('updateTemplate', { random: data });
});
});
server.listen(port, () => {
logger.info(`Https server is listening on https://localhost:${port}`);
});
Client-side:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Websocket Secure Connection</title>
</head>
<body>
<div>
<button id='btn'>Send Message</button>
<ul id='messages'></ul>
</div>
<script src='../../../node_modules/socket.io-client/dist/socket.io.js'></script>
<script>
window.onload = function onload() {
const socket = io('https://localhost:3001');
socket.on('news', function (data) {
console.log(data);
});
socket.on('updateTemplate', function onUpdateTemplate(data) {
console.log(data)
createMessage(JSON.stringify(data));
});
const $btn = document.getElementById('btn');
const $messages = document.getElementById('messages');
function sendMessage() {
socket.emit('updateTemplate', Math.random());
}
function createMessage(msg) {
const $li = document.createElement('li');
$li.textContent = msg;
$messages.appendChild($li);
}
$btn.addEventListener('click', sendMessage);
}
</script>
</body>
</html>
For enterprise applications it should be noted that you should not be handling https in your code. It should be auto upgraded via IIS or nginx. The app shouldn't know about what protocols are used.
This is my nginx config file and iosocket code. Server(express) is listening on port 9191. It works well: nginx config file:
server {
listen 443 ssl;
server_name localhost;
root /usr/share/nginx/html/rdist;
location /user/ {
proxy_pass http://localhost:9191;
}
location /api/ {
proxy_pass http://localhost:9191;
}
location /auth/ {
proxy_pass http://localhost:9191;
}
location / {
index index.html index.htm;
if (!-e $request_filename){
rewrite ^(.*)$ /index.html break;
}
}
location /socket.io/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://localhost:9191/socket.io/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
ssl_certificate /etc/nginx/conf.d/sslcert/xxx.pem;
ssl_certificate_key /etc/nginx/conf.d/sslcert/xxx.key;
}
Server:
const server = require('http').Server(app)
const io = require('socket.io')(server)
io.on('connection', (socket) => {
handleUserConnect(socket)
socket.on("disconnect", () => {
handleUserDisConnect(socket)
});
})
server.listen(9191, function () {
console.log('Server listening on port 9191')
})
Client(react):
const socket = io.connect('', { secure: true, query: `userId=${this.props.user._id}` })
socket.on('notifications', data => {
console.log('Get messages from back end:', data)
this.props.mergeNotifications(data)
})
Depending on your needs, you could allow both secure and unsecure connections and still only use one Socket.io instance.
You simply have to instanciate two servers, one for HTTP and one for HTTPS, then attach those servers to the Socket.io instance.
Server side :
// needed to read certificates from disk
const fs = require( "fs" );
// Servers with and without SSL
const http = require( "http" )
const https = require( "https" );
const httpPort = 3333;
const httpsPort = 3334;
const httpServer = http.createServer();
const httpsServer = https.createServer({
"key" : fs.readFileSync( "yourcert.key" ),
"cert": fs.readFileSync( "yourcert.crt" ),
"ca" : fs.readFileSync( "yourca.crt" )
});
httpServer.listen( httpPort, function() {
console.log( `Listening HTTP on ${httpPort}` );
});
httpsServer.listen( httpsPort, function() {
console.log( `Listening HTTPS on ${httpsPort}` );
});
// Socket.io
const ioServer = require( "socket.io" );
const io = new ioServer();
io.attach( httpServer );
io.attach( httpsServer );
io.on( "connection", function( socket ) {
console.log( "user connected" );
// ... your code
});
Client side :
var url = "//example.com:" + ( window.location.protocol == "https:" ? "3334" : "3333" );
var socket = io( url, {
// set to false only if you use self-signed certificate !
"rejectUnauthorized": true
});
socket.on( "connect", function( e ) {
console.log( "connect", e );
});
If your NodeJS server is different from your Web server, you will maybe need to set some CORS headers. So in the server side, replace:
const httpServer = http.createServer();
const httpsServer = https.createServer({
"key" : fs.readFileSync( "yourcert.key" ),
"cert": fs.readFileSync( "yourcert.crt" ),
"ca" : fs.readFileSync( "yourca.crt" )
});
With:
const CORS_fn = (req, res) => {
res.setHeader( "Access-Control-Allow-Origin" , "*" );
res.setHeader( "Access-Control-Allow-Credentials", "true" );
res.setHeader( "Access-Control-Allow-Methods" , "*" );
res.setHeader( "Access-Control-Allow-Headers" , "*" );
if ( req.method === "OPTIONS" ) {
res.writeHead(200);
res.end();
return;
}
};
const httpServer = http.createServer( CORS_fn );
const httpsServer = https.createServer({
"key" : fs.readFileSync( "yourcert.key" ),
"cert": fs.readFileSync( "yourcert.crt" ),
"ca" : fs.readFileSync( "yourca.crt" )
}, CORS_fn );
And of course add/remove headers and set the values of the headers according to your needs.
check this.configuration..
app = module.exports = express();
var httpsOptions = { key: fs.readFileSync('certificates/server.key'), cert: fs.readFileSync('certificates/final.crt') };
var secureServer = require('https').createServer(httpsOptions, app);
io = module.exports = require('socket.io').listen(secureServer,{pingTimeout: 7000, pingInterval: 10000});
io.set("transports", ["xhr-polling","websocket","polling", "htmlfile"]);
secureServer.listen(3000);
This is how I managed to set it up with express:
var fs = require( 'fs' );
var app = require('express')();
var https = require('https');
var server = https.createServer({
key: fs.readFileSync('./test_key.key'),
cert: fs.readFileSync('./test_cert.crt'),
ca: fs.readFileSync('./test_ca.crt'),
requestCert: false,
rejectUnauthorized: false
},app);
server.listen(8080);
var io = require('socket.io').listen(server);
io.sockets.on('connection',function (socket) {
...
});
app.get("/", function(request, response){
...
})
I hope that this will save someone's time.
Update : for those using lets encrypt use this
var server = https.createServer({
key: fs.readFileSync('privkey.pem'),
cert: fs.readFileSync('fullchain.pem')
},app);
If your server certificated file is not trusted, (for example, you may generate the keystore by yourself with keytool command in java), you should add the extra option rejectUnauthorized
var socket = io.connect('https://localhost', {rejectUnauthorized: false});
Source: Stackoverflow.com