Node.js Socket.io on Heroku throws H13 - node.js

Deploying my app on Heroku breaks websockets protocol.
It works on localhost, not on Heroku. In browser, I get :
WebSocket connection to 'wss://node-omi.herokuapp.com/socket.io/?EIO=2&transport=websocket&sid=*' failed: Error during WebSocket handshake: Unexpected response code: 503
On the server side, I get in logs:
2014-08-12T15:05:24.761611+00:00 heroku[router]: at=error code=H13 desc="Connection closed without response" method=GET path="/socket.io/?EIO=2&transport=websocket&sid=****" host=node-omi.herokuapp.com request_id=* fwd="*" dyno=web.1 connect=3ms service=3ms status=503 bytes=864
This is a part of my server script:
var express = require('express'),
http = require('http'),
socket_io = require('socket.io');
var app = express(),
server = http.Server(app),
io = new socket_io(server).of('/test');
io.on('connection', this.connection.bind(this));
app.get('/client.html', function(req, res, next) { ... });
app.use('/', express.static(__dirname + '/public', {'index': ['index.html']}));
server.listen(process.env.PORT || 5000);
What's wrong with my code ? Does anyone succeed in building a socket.io server with Express 4.8.3 and Socket.io 1.0.6 ? The socket works with polling, but I really need websockets to work. Thanks for replies.

Websocket support on Heroku has since gone from beta to official support, so at the time of this post the fix might have been to simply labs:enable websockets But you should no longer need to do this, you get websockets out of the box.
Warning!
But as a warning to others reading this question, using socket.io on Heroku can be problematic because of a disagreement around sticky sessions
Heroku explicitly stands against them but (short of a rather crappy workaround described below) socket.io requires them:
https://github.com/Automattic/engine.io/issues/261
^The tl;dr if this very long thread is that before v1.0, socket.io worked with non-sticky services like Heroku, you just needed to use a redis adapter to manage shared state across your dynos. This was removed in 1.0 and the socket.io team is apprehensive to put it back due to the code maintenance cost. The github issue asking to have it back has been closed with the message that they'll be happy to look into it as soon as someone wants it badly enough to make a PR.
Workaround!
There is a workaround which is to restrict socket.io to using websocket transport only, which does not require sticky sessions. Eg:
Server:
io.set('transports', ['websocket']);
Client
var socket = io.connect(host, {
transports: ['websocket']
});
However, this removes much of the benefit that socket.io 1.0 brings with its use of engine.io.
There are other websocket frameworks like Faye that work very nicely on Heroku.

Related

Problem deploying Apollo Express server to Heroku: "GET query missing."

I wrote a simple GraphQL server using Apollo Express and deployed it to Heroku. After some messing around with Procfiles and the like, it built OK.
When I hit the main URL https://limitless-atoll-59109.herokuapp.com I get the error
Cannot GET /
OK, then I thought the Express server must just be looking for a get on the graphql endpoint. But when I hit https://limitless-atoll-59109.herokuapp.com/graphql I get
GET query missing.
Do I need to include a port in the url? I've got the port set correctly in the code
const PORT = process.env.PORT || 4000
app.listen({port: PORT}, () =>
console.log(`Server ready at http://localhost:${PORT}${server.graphqlPath}`)
);
but I don't think I need to include it when accessing the server on Heroku, do I?
For what it's worth, this is the error in the error logs
019-05-08T17:07:41.492327+00:00 heroku[router]: at=info method=GET
path="/graphql" host=limitless-atoll-59109.herokuapp.com
request_id=b6171835-aac4-4b45-8a7b-daebbb3167ed fwd="139.47.21.74"
dyno=web.1 connect=0ms service=900ms status=400 bytes=196
protocol=https
Thanks for any help!
The answer is that it is the url that ends in /graphql that one wants. However, when you hit that url from the browser, it attempts to load the playground, and fails, as the playground is disabled by default in production.
However, you can make graphql calls against that url from your client app and it works fine.
If you are using apollo server you can just enable it in production:
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
playground: true,
});

Websocket on Azure with nodeJS

I manage to make websocket work on a nodeJS+express application on azure.
However it is using polling instead of websocket, anyone know why is that?
Here are the config.
Client Side
socket = io.connect(url, {'transports':['websocket', 'polling']);
Server side
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() {
console.info('Express server started');
});
var io = require('socket.io').listen(server, {'transports': ['websocket', 'polling']});
I am using socket.io 1.3.6
EDIT:
On Azure I have websocket and the Always On setting ON.
It's also not a the free package.
OK. I also have a socketIO app hosted on an azure website, and the web sockets does work as expected. Did you check this article out? Enabling Websockets for Socket.io Node apps on Microsoft Azure
Here's the important part:
Note that we say "webSocket enabled=false" in this web.config. This is
confusing, but makes sense when you realize we're saying "disable
Websockets in IIS and let node (or whomever) downstream handle it"
I ended up downgrading socket.io to 1.3.5 to get websockets to work on Azure (iisnode)

Errors going to 2 dynos on Heroku with socket.io / socket.io-redis / rediscloud / node.js

I have a node.js / socket.io app running on Heroku. I am using socket.io-redis with RedisCloud to allow users who connect to different dynos to communicate, as described here.
From my app.js:
var express = require('express'),
app = express(),
http = require('http'),
server = http.createServer(app),
io = require('socket.io').listen(server),
redis = require('redis'),
ioredis = require('socket.io-redis'),
url = require('url'),
redisURL = url.parse(process.env.REDISCLOUD_URL),
And later in app.js ...
var sub1 = redis.createClient(redisURL.port, redisURL.hostname, {
no_ready_check: true,
return_buffers: true
});
sub1.auth(redisURL.auth.split(":")[1]);
var pub1 = redis.createClient(redisURL.port, redisURL.hostname, {
no_ready_check: true,
return_buffers: true
});
pub1.auth(redisURL.auth.split(":")[1]);
var redisOptions = {
pubClient: pub1,
subClient: sub1,
host: redisURL.hostname,
port: redisURL.port
};
if (io.adapter) {
io.adapter(ioredis(redisOptions));
console.log("mylog: io.adapter found");
}
It is kind of working -- communication is succeeding between dynos.
Three issues that happen with 2 dynos but not with 1 dyno:
1) There is a login prompt which comes up and works reliably with 1 dyno but is hit-and-miss with 2 dynos -- may not come up and may not work if it does come up. It is (or should be) triggered by the io.sockets.on('connection') event.
2) I'm seeing a lot of disconnects in the server log.
3) Also lots of errors in the client console on Chrome, for example:
socket.io.js:5039 WebSocket connection to 'ws://example.mydomain.com/socket.io/?EIO=3&transport=websocket&sid=F8babuJrLI6AYdXZAAAI' failed: Error during WebSocket handshake: Unexpected response code: 503
socket.io.js:2739 POST http://example.mydomain.com/socket.io/?EIO=3&transport=polling&t=1419624845433-63&sid=dkFE9mUbvKfl_fiPAAAJ net::ERR_INCOMPLETE_CHUNKED_ENCODING
socket.io.js:2739 GET http://example.mydomain.com/socket.io/?EIO=3&transport=polling&t=1419624842679-54&sid=Og2ZhJtreOG0wnt8AAAQ 400 (Bad Request)
socket.io.js:3318 WebSocket connection to 'ws://example.mydomain.com/socket.io/?EIO=3&transport=websocket&sid=ITYEPePvxQgs0tcDAAAM' failed: WebSocket is closed before the connection is established.
Any thoughts or suggestions would be welcome.
Yes, like generalhenry said, the issue is that Socket.io requires sticky sessions (meaning that requests from a given user always go to the same dyno), and Heroku doesn't support that.
(It works with 1 dyno because when there's only 1 then all requests go to it.)
https://github.com/Automattic/engine.io/issues/261 has a lot more good info, apparently web sockets don't really require sticky sessions, but long-polling does. It also mentions a couple of potential work-arounds:
Roll back to socket.io version 0.9.17, which tries websockets first
Only use SSL connections which, makes websockets more reliable (because ISP's and corporate proxies and whatnot can't tinker with the connection as easily.)
You might get the best results from combining both of those.
You could also spin up your own load balancer that adds sticky session support, but by that point, you're fighting against Heroku and might be better off on a different host.
RE: your other question about the Node.js cluster module: it wouldn't really help here. It's for using up all of the available CPU cores on a single server/dyno,

Heroku socket.io sample error in Express 4.0

So I'm trying to use the sample heroku application: https://github.com/lstoll/socket-io-chat-heroku as a template to build my own socket.io application, but I'm running on Express 4.0, Node 0.10.x, and Socket.io 0.9.16.
Now the problem is that when I run my server, everything is fine, but when I run my client, I get the following error:
Uncaught ReferenceError: require is not defined socket.io.js:12
Uncaught ReferenceError: io is not defined chat2:2
My relevant server code is as follows:
var app = express();
var http = require('http');
var server = http.createServer(app);
var sio = require('socket.io');
var port = 3000 || process.env.PORT;
server.listen(port);
var io = sio.listen(server);
io.sockets.on('connection'), function(socket) {
...
});
On my client side, I have the following:
I've tried both (this is in jade, by the way):
script(src='/socket.io/socket.io.js') OR script(src='http://localhost:3000/socket.io/socket.io.js')
var socket = io.connect() OR var socket = io.connect('http://localhost:3000')
Neither of these options have worked, always resulting in an error on the client side.
Is there something special to do for Express 4.0? I've asked a very similar question here: Node.js /socket.io/socket.io.js not found express 4.0 but this is another attempt at getting chat to work with a different template.
Update and Edit: after some work, I was able to deploy a heroku app using express 4.0 and socket.io, at: http://salty-escarpment-7398.herokuapp.com/chat.
The problem now is to integrate it back into my current app, and after much work, I now am getting an error:
22:19:56 web.1 | GET /socket.io/?EIO=2&transport=polling 404 26ms - 1.67kb
22:19:59 web.1 | GET /socket.io/?EIO=2&transport=polling 404 25ms - 1.67kb
I have:
io.set('transports', ['xhr-polling']);
io.set('polling duration', 10);
To set it to xhr-polling, and my server code is pretty much identical to what was above. The page loads, though, and it's only when trying to send a chat that nothing happens and the 404 starts appearing.
Thanks
By default Socket.IO 1.0.4 allows polling and websocket transports. You removed polling transport ! return the polling transport back:
io.set('transports', ['websocket',
'flashsocket',
'htmlfile',
'xhr-polling',
'jsonp-polling',
'polling']);

How do I Scale Heroku app that uses Nowjs?

Scaling my web app to 2 web proceses on Heroku breaks Nowjs/Sockets.io.
I'm using full stack that is new to me and everything works great until I add a 2nd web process. Node.js still responds fine but Nowjs stops responding. I get 503 responds in the browser and
GET musicbacon.com/socket.io/1/?t=1339117661910 dyno=web.2 queue=0 wait=0ms service=2ms status=200 bytes=82
2012-06-08T01:07:42+00:00 heroku[router]: Error H13 (Connection closed without response) -> GET musicbacon.com/socket.io/1/websocket/12401614301555103827 dyno=web.2 queue= wait= service= status=503 bytes=
on the server.
I think that Nowjs (which is an abstraction layer on top of Socket.io) creates it's own server along side my Node.js server. Scaling Heroku spins up a new Node.js server VM but since Now.js isn't yet compatible with distributed environments it just dies.
Hopefully I'm missing something and I can scale Now.js because 1 Heroku web process can only handle about 60 users at a time in my experience.
Full stake is:
Heroku
Node.js
Express
Jade
Now.js
Postgres
Error H13 reference: https://devcenter.heroku.com/articles/error-codes#h13__connection_closed_without_response
WebSockets don't work on Heroku, you need to change your Socket.IO configuration to disable WS:
https://devcenter.heroku.com/articles/using-socket-io-with-node-js-on-heroku
https://devcenter.heroku.com/articles/request-timeout#longpolling_and_streaming_responses
https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO

Resources