I'm running a simple websocket server in nodejs
const WebSocket = require('ws');
const wss = new WebSocket.Server({ host: '0.0.0.0', port: 13000 });
wss.on('connection', function connection(ws) {
console.log("new connection");
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.on('error', function(e) {
console.log('error', e);
});
ws.on('close', function(e) {
console.log('close', e);
});
ws.send('something');
});
I can connect to the server using:
s=new WebSocket('ws://localhost:13000')
and everything is fine. But if I try to connect from another computer:
s=new WebSocket('ws://serveripaddress:13000')
The server shows
new connection
close 1006
So I know that the client connects, but it then immediately disconnects. I am totally lost here, don't know how to diagnose my problem. Any ideas?
UPDATE
These scenarios work:
two ec2 instances: works
two linux VMs communicating over a host-only network: works
And these fail:
windows host machine and VM (host only network): fail
an ec2 instance and one of my VM's: fail
Here's the weird part, if I make a simple socket server using 'net', ALL scenarios above work! So it seems like its still a networking issue (since websockets work in some scenarios) but it is a networking issue specific to websockets (because socket server works where websocket doesnt)!
I dont have access to the firewall that my windows host machine is on so if that's the issue then I'm stuck. But I will try this all on a different host machine where I have access to the firewall.
But if it was the firewall, I would except it to work between the windows host and the VM because they are on their own virtual network...
Related
Context
I've been working on GraphQL subscriptions for my team and have been trying to set up an API Gateway WebSocket API that listens to incoming requests and forwards the subscriptions to our backend services. The integration for the WebSocket API is a VPC Link that targets an ECS cluster which is running a server. One issue with VPC Link integrations for WebSocket APIs is that the WebSocket request is automatically downgraded to HTTP. Therefore, I've been exploring an alternative solution involving a proxy HTTP server which listens to the downgraded WebSocket request and then establishes a WebSocket request with the actual WebSocket server. The proxy HTTP server is defined with ExpressJS and the WebSocket server uses graphql-ws (similar to this example from Apollo https://www.apollographql.com/docs/apollo-server/data/subscriptions/#enabling-subscriptions, the full server code example listed in item #7).
Problem
My problem is that I am unable to successfully establish a WebSocket connection between the two servers when testing the docker containers locally. The current setup is two local docker containers (HTTP proxy and WebSocket server) that are running on the default "bridge" network. Once both servers are running, I use Postman to send a PUT request to the HTTP proxy server. Ideally, the HTTP proxy server should then open a WebSocket connection with the WebSocket server. However, this leads to a 4406 error: Subprotocol not acceptable. graphql-ws uses the graphql-transport-ws protocol (https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) and I've tried to change the protocol defined in the WebSocket class constructor but 4406 error is happening regardless of whether or not I specify a protocol on the new WebSocket.
I've tested the WebSocket server by sending requests directly to it and it works as expected. This is why I believe the issue is the HTTP proxy server and not the WebSocket server. Here is a TypeScript code sample that demonstrates how I'm creating the WebSocket on the proxy server:
import express from 'express';
import WebSocket from 'ws';
const app = express();
app.put('/', (req, res) => {
const body = req.body ? req.body : {};
const socket = new WebSocket(wsEndpoint, {
headers: {
'Sec-WebSocket-Version': 13,
'Sec-WebSocket-Key': 'Ab2SMOa5hg0YUyC1OELtyQ==',
Connection: 'Upgrade',
Upgrade: 'websocket',
'Sec-WebSocket-Extensions':
'permessage-deflate; client_max_window_bits',
Host: 'localhost',
},
protocol: 'graphql-transport-ws',
});
socket.on('error', (err) => {
console.log(JSON.stringify(err));
});
socket.on('open', (s: any) => {
console.log('socket open');
});
socket.on('close', (code, reason) => {
console.log(`socket close: ${code}, reason: ${reason.toString()}`);
});
socket.on('error', (error) => {
console.log('socket error');
console.log(error);
});
socket.on('upgrade', (socket: any, request: any) => {
console.log('socket upgrade');
console.log(request);
});
res.status(200).json({
message: 'Connection established to WebSocket server',
});
});
Log snippet:
Proxy server listening on port 80 <-- app.listen() on port 80
socket upgrade <-- starts once I make the PUT request via Postman
socket open
socket close: 4406, reason: Subprotocol not acceptable
What I've Tried
So far I've tried to open the WebSocket both with a protocol in the WebSocket class constructor, and without a value. However, the 4406 error still shows up in both cases.
I've also tried to follow advice that was discussed in this question: How to create websocket connection between two Docker containers. When I create a user defined network, I still run into this exact same 4406 error.
I suspect that it might be that I'm missing something in the WebSocket constructor or some low-level knowledge about how graphql-ws works.
how are you?
I have a question regarding external access to my (dockerized or not) application.
I have a simple ping-pong socket communication using socket.io with the following code files:
server.js
let Server = require('socket.io')
const io = new Server().attach(8090)
io.on('connection', (socket) => {
console.log('connection')
socket.on('message', (msg) => {
console.log('< ' + msg)
console.log('> pong')
socket.emit('event', 'pong')
})
})
client.js
let host = process.argv[2]
let io = require("socket.io-client");
let client = io.connect(`http://${host}`)
let connected = false
client.on('connect', () => {
connected = true
})
client.on('event', (msg) => {
console.log('< ' + msg)
ping()
})
function ping () {
if (!connected) {
setTimeout(ping, 1000)
} else {
setTimeout(() => {
console.log('> ping')
client.emit('message', 'ping');
}, 1000)
}
}
ping()
with my server up and running, I can execute the client to test the communication like so:
$ node client.js localhost:8090
and the output is similar to this:
> ping
< pong
> ping
< pong
> ping
< pong
... and so on
Everything works normally when I test this (both client and server) outside my EC2 machine, but when I deploy the server.js to the cloud and run it, I can't reach it from outside.
When I bind a nginx web service on port 80 or 81 and proxy every request to the application, everything starts working fine from outside the cloud, but still does not work on other ports.
My security group is fully open (for testing purposes) (inbound and outbound allowing all traffic from and to anywhere (0.0.0.0/0, ::/0) on all ports) (screenshot attached).
Things that may be useful for troubleshooting:
When I try a NodeJS http server (without nginx) using node on the same port, it does not work either (works only on 80 and 81)
I am sure my machine is on the 'fully-open-insecure' security group with all traffic allowed.
My application is binding to all addresses because I can connect to it using the public address or localhost from inside the cloud machine.
It only works on port 80 and 81 when I proxy_pass connections from a nginx web server that is on the same machine as the NodeJS application
any help or troubleshooting suggestion will be very appreciated.
thank you very much for your time
Below is a screenshot with the security group definitions:
My problem was solved and this is embarrassing. My local network was not allowing outbound connections on the ports I mentioned.
If you have the same problem, I recommend to test your local network. Also, check your iptables rules
I am trying to write a text file from NodeJs. I have server running on my laptop. Currently, i am running client on my laptop and it works fine. But if i run same NodeJs client on Linux running on raspberrypi, it doesn't write on file or neither it gives any error.
I have the following code for client
var ioC = require('socket.io-client'),
ioClient = ioC.connect('http://localhost:4000'),
fs = require('fs'),
os = require('os');
ioClient.on('connect', function () { console.log("socket connected"); });
ioClient.on('ChangeState', function(msg){
console.log(msg);
fs.writeFile('server.txt', JSON.stringify(msg), function (err){
if (err) return console.log(err);
});
});
Can anybody please help me what can be the issue with this?
You are connecting to localhost which won't work if the client is on a different machine. You need to change the server to listen to the ip address your server has in your network, and also need to let your client connect to this ip. You can get the ip by running ifconfig in your terminal. Then (depending on wireless or wired connection) look for something like (usually the last paragraph):
and create the server on this ip. E.g.
192.168.178.30:4000
and connect to the same address from your client.
To find your ip on windows, refer to this guide
I have developed a simple chat using socket.io on a vagrant environment and it works correctly.
When I try to run the chat on the production environment, the nodejs/socket.io server runs but the client doesn't even fire the connect event.
The only thing I've modified is, in the client side:
var socket = io.connect('http://localhost:3000');
to:
var socket = io.connect('http://ip_address:3000');
This is the server code:
var app = require('http').createServer(handler);
var io = require('socket.io')(app);
app.listen(3000, function() {
console.log('Server is running');
});
function handler(req, res) {
res.writeHead(200);
res.end('');
}
io.on('connection', function(socket) {
socket.on('subscribe', function() {
console.log('subscribe request has arrived');
console.log(socket.id);
});
});
If it worked locally and stopped working after deployment, it's probably a network issue - firewall, blocked port, linux enforcing or something of that sort.
To find the source, go to the host server and try connecting the port
telnet 127.0.0.1 3000
Did you get 'Connection refused' error? if so which server is running the code? make sure firewall service is either off or with exceptions for port 3000. on linux check enforcing .
If the connection succeed, test the connection from your local pc
telnet ip_address 3000
If it fails than test access to external service (for example portquiz.net) This way you will know if the issue is with your local pc or network, or the remove server.
I have a desktop app in Adobe Air (flex).
I have used flashSocket.io library in my app to communicate with socket.io on node.js server.
It works perfectly for most of the clients.
But for some random client the flex app is not able to create a connection with the socket.io server. It constantly throws connection error and close error on flex. The clients are not behind any firewalls or proxy server.
The console has warning like
Web Socket Connection Invalid
I guess this for those clients who are not able to connect.
Since its working for majority of the users i don't know where should i look into. Also, i am unable to reproduce this on my side.
Here's the Server Code:
var io = require('socket.io').listen(app);
app.listen(8080, function() {
console.log('%s listening at %s', app.name, app.url);
});
io.configure(function() {
io.set('transports', ['flashsocket']);
io.set('flash policy port', 843);
});
Flex code:
socket = new FlashSocket("http://domain.com:8080/");
socket.addEventListener(FlashSocketEvent.CONNECT, onConnect);
socket.addEventListener(FlashSocketEvent.MESSAGE, onMessage);
socket.addEventListener(FlashSocketEvent.CLOSE, onDisconnect); //only close and connect_error event is always fired for certain clients
socket.addEventListener(FlashSocketEvent.CONNECT_ERROR, onConnectError);
Any help would be highly appreciated.
Use a different port, we're using 443.