Socket.io server over https - https

I tried to convert the socket IO getting started example to https like this:
const fs = require('fs');
const app = require('https').createServer({
key: fs.readFileSync("privkey.pem"),
cert: fs.readFileSync("cert.pem"),
ca: fs.readFileSync("fullchain.pem"),
}, handler)
const io = require('socket.io')(app);
app.listen(443);
function handler (req, res) {
res.writeHead(200);
res.end();
}
io.on('connection', (socket) => {
socket.emit('news', { hello: 'world' });
socket.on('my other event', (data) => {
console.log(data);
});
});
But running the following in my browser does not work:
const ws = new WebSocket("wss://example.com");
But I received the following error:
Firefox can’t establish a connection to the server at wss://example.com/.
I tried to debug this by running this curl command:
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: example.com" -H "Origin: https://example.com " https://example.com
and the result was:
curl: (52) Empty reply from server
None of the console logging code is reached and there are no errors on the node script while doing all of this.
Why can't I connect to my websocket server?

According to doc:
Socket.IO is NOT a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the packet id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server either.
So this is not correct:
const ws = new WebSocket("wss://example.com");
You should use:
const socket = io('https://your-server-address');

Related

Can't get connection to socket server

I'm fairly new to a lot of this stuff and am trying to figure it out.
I have a hosted domain at <my.domain.com>. I host a game at this address that users can go to that address and the game loads in the browser for them.
On the same server I am running an Express nodejs (we'll call this HTTP SERVER) server to receive HTTP requests.
Also on the same server I am running a socket server using the Socket.io (we'll call this SOCKET SERVER) library.
HTTP SERVER can connect to SOCKET SERVER via localhost:<port> and they can communicate back and forth. I can send requests from my mobile device to HTTP SERVER which forwards those request to SOCKET SERVER and get a response back on the mobile device.
My problem now is I need to create another connection to SOCKET SERVER from my hosted game at <my.domain.com>. However, when I attempt to connect to localhost:<port> like I do from HTTP SERVER I get an ERR_CONNECTION_REFUSED error. I am assuming this has to do with with the host name being different. I've attempted to add
app.use(function(req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
});
But that doesn't seem to help. I'm not really sure where to go from here.
Socket server app.js
const app = require('express')();
const server = require('http').Server(app);
const io = require('socket.io')(server);
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
});
server.listen(8082);
io.on('connection', (socket) => {
console.log(`Socket server 'connection' event`);
});
Code in HTTP SERVER that does properly connect and send/receive messages
var socket = require('socket.io-client')('http://localhost:8082');
socket.on('connect', () => {
console.log(`HTTP server - 'connect' event to socket server`);
});
This is a javascript file that the game loads as an add-on. Hooks is provided by the game as an EventEmitter. I do not have direct access to the HTML pages the game displays, though I can manipulate them via this javascript add-on file.
let socket;
// a game hook when it's initialized
Hooks.on("init", function() {
// don't have direct access to game pages, so create a script tag and load
// the socket.io client library
const scriptRef = document.createElement('script');
scriptRef.setAttribute('type', 'text/javascript');
scriptRef.setAttribute('onload', 'window.socketLibraryLoaded()');
scriptRef.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js');
document.getElementsByTagName('head')[0].appendChild(scriptRef);
});
// handler for when library is loaded
window.socketLibraryLoaded = () => {
log('Socket library loaded');
// i assume this address is wrong since the host of the game is <my.domain.com> and it's trying to connect to localhost
socket = io('https://localhost:8082');
socket.on('connect', () => {
log('Connected to socket server');
});
socket.on('connect_error', error => {
log(error);
});
}
So after banging my head on the wall for more than 10 hours over this I finally found the issue. And of course a simple user error.
The CORs error wasn't really the problem. I was getting that error because the NGINX proxy was erroring which caused the proper headers not to get sent back so the browser showed that error.
The issue was that in one place in my NGINX configuration I was using 127.0.0.0 instead of 127.0.0.1

How to call a https POST method using gatewayscript in IBM Bluemix APIConnect

I am trying to call another API inside Bluemix or any other HTTPS post method using a gateway script inside IBM Bluemix (API Connect) using the code below:
var urlopen = require('urlopen');
var options = {
target: 'https://pokemons.mybluemix.net/api/pokemons/1',
method: 'POST',
headers: {},
contentType: 'application/json',
timeout: 60,
data: {"Message": "DataPower GatewayScript"}
};
urlopen.open(options, function(error, response) {
if (error) {
// an error occurred during the request sending or response header parsing
session.output.write("urlopen error: "+JSON.stringify(error));
} else {
// get the response status code
var responseStatusCode = response.statusCode;
var responseReasonPhrase = response.reasonPhrase;
console.log("Response status code: " + responseStatusCode);
console.log("Response reason phrase: " + responseReasonPhrase);
// reading response data
response.readAsBuffer(function(error, responseData){
if (error){
throw error ;
} else {
session.output.write(responseData) ;
apim.output('application/json');
}
});
}
});
But I am getting the following error:
{
"httpCode": "500",
"httpMessage": "Internal Server Error",
"moreInformation": "URL open: Cannot create connection to 'https://pokemons.mybluemix.net/api/pokemons/1', status code: 7"
}
Looks like there is some issue with the SSL Connections. If so, how can I get the SSL Details for the default Sandbox Catalog in IBM Bluemix API Connect? Or, how can I make the HTTPS POST calls to the above sample URL?
Since Version 5.0.6:
IBM API Connect 5.0.x
Forward SSLProxy (and Crypto) is replaced with SSLClient. These new profiles support ephemeral ciphers (DHE and ECDHE), perfect forward secrecy, and Server Name Indication (SNI) extension. Note that DHE ciphers in DataPower SSLServerProfile use 2048-bit DH parameters (as server) and accept 1024-bit DH parameters (as client).
In order for you specific example to work on API Connect using HTTPS you need to specify the sslClientProfile.
For example:
var urlopen = require('urlopen');
var options = {
target: 'https://pokemons.mybluemix.net/api/pokemons/1',
method: 'POST',
headers: {},
contentType: 'application/json',
timeout: 60,
sslClientProfile: 'webapi-sslcli-mgmt',
data: {"Message": "DataPower GatewayScript"}
};

Unable to see http traffic from/to my NodeJS app in Charles [mac]

I am running Charles to inspect HTTP traffic between a node js client and a service running locally on my machine (a Mac). I am able to access the service but don't see any trace in Charles. I have tried replacing localhost with my machine's IP name but still no trace. If I type the service URL in Chrome I do see a trace. Anyone knows how to fix this?
Here is my nodejs code:
var thrift = require('thrift'); // I use Apache Thrift
var myService = require('./gen-nodejs/MyService'); // this is code generated by thrift compiler
var transport = thrift.TBufferedTransport();
var protocol = thrift.TBinaryProtocol();
var connection = thrift.createHttpConnection("localhost", 5331, {
transport : transport,
protocol : protocol,
path: '/myhandler',
});
connection.on('error', function(err) {
console.log(err);
});
// Create a client with the connection
var client = thrift.createHttpClient(myService, connection);
console.log('calling getTotalJobCount...');
client.getTotalJobCount(function(count)
{
console.log('total job count = ' + count);
});
and my proxy settings:
fixed this myself with help of this link. Charles intercepts the traffic crossing the system proxy which is 127.0.0.1:8888 on my mac. Here is proper code:
// give path to the proxy in argument to createHttpConnection
var connection = thrift.createHttpConnection('127.0.0.1', 8888, {
transport : transport,
protocol : protocol,
path: 'http://localhost:5331/myhandler', // give the actual URL you want to connect to here
});
In addition need to use thrift.TBufferedTransport instead of thrift.TBufferedTransport() and thrift.TBinaryProtocol instead of thrift.TBinaryProtocol()

Websocket server returned empty reply

I ran a websocket server using socket.io:
var http = require('http'),
fs = require('fs'),
// NEVER use a Sync function except at start-up!
index = fs.readFileSync(__dirname + '/index.html');
// Send index.html to all requests
var app = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(index);
});
// Socket.io server listens to our app
var io = require('socket.io').listen(app);
// Send current time to all connected clients
function sendTime() {
io.emit('time', { time: new Date().toJSON() });
}
// Send current time every 10 secs
setInterval(sendTime, 10000);
// Emit welcome message on connection
io.on('connection', function(socket) {
// Use socket to communicate with this particular client only, sending it it's own id
socket.emit('welcome', { message: 'Welcome!', id: socket.id });
socket.on('i am client', console.log);
});
app.listen(3000);
This server works fine withe a simple socket-io client:
var socket = require('socket.io-client')('http://127.0.0.1:1337');
socket.on('connect', function(){});
socket.on('time', function(data){console.log(data);});
socket.on('disconnect', function(){});
I tried to use cURL to send the connection request but it failed:
curl --verbose -i -N -H "Upgrade: websocket" -H "Connection: Upgrade" -H "Host: 127.0.0.1" -H "Origin: http://127.0.0.1" http://127.0.0.1:3000/
* Connected to 127.0.0.1 (127.0.0.1) port 3000 (#0)
> GET /socket.io HTTP/1.1
> User-Agent: curl/7.37.1
> Accept: */*
> Upgrade: websocket
> Connection: Upgrade
> Host: 127.0.0.1
> Origin: http://127.0.0.1
>
* Empty reply from server
* Connection #0 to host 127.0.0.1 left intact
curl: (52) Empty reply from server
If I removed the "Upgrade: websocket", then the server will send back the html page. The "Upgrade: websocket" is supposed to tell the server to upgrade the HTTP connection to websocket connection. Why it didn't work?

SignalR and failed websocket connection, but still works

I am currently getting this error below in Chrome console, but it still connects successfully with SignalR. Any reason why I am getting this error?
JS Hub Connection
scheduleHub = $.connection.scheduleHub;
scheduleHub.client.viewing = function (name, message) {
app.showWarning(message, name, function () {
app.refreshHash();
});
};
if ($.connection.hub && $.connection.hub.state === $.signalR.connectionState.disconnected) {
$.connection.hub.qs = { "eventid": options.eventId };
$.connection.hub.start()
.done(function () {
alert('Connected');
//scheduleHub.server.viewing('wow', 'test');
})
.fail(function() { alert('Could not Connect!'); });
}
Chrome Console
WebSocket connection to 'ws://localhost:2222/signalr/connect?transport=webSockets&clientProtocol=1.4&eventid=23919&connectionToken=CV3wchrj88t6FdjgA%2BREdzEDIw0rhW6r2aUrb%2BI8qInsb3Y9BqQSOscPxfAZ2g0Dxl704usqdBBn%2BNSFKpjVNOtwASndOweD1kGWPCkWEbtJBMu%2B&connectionData=%5B%7B%22name%22%3A%22schedulehub%22%7D%5D&tid=5' failed: Error during WebSocket handshake: Unexpected response code: 500
Web Sockets initially starts by negotiating the websockets connection over HTTP. During this HTTP handshake, the web server probably raised an exception, anyway, it returns HTTP Status Code 500. Without a successful HTTP response, Chrome is unable to continue negotiating the web sockets connection.
Since SignalR works over multiple transports, and not just websockets, once websockets connection failed, it will have automatically have switched to try some other transport, like forever frame or polling, which is why your connection still works.

Resources