websockets / ws

Simple to use, blazing fast and thoroughly tested WebSocket client and server for Node.js
MIT License
21.34k stars 2.3k forks source link

Error: Unexpected server response: 404 #2226

Closed ru-zxr closed 1 month ago

ru-zxr commented 1 month ago

Is there an existing issue for this?

Description

Frontend: React Native with Expo Backend: Node.js with Koa

I encountered difficulties establishing a WebSocket connection. I tried all three options provided by WebSocket to create a new server instance. Only initializing the server on a different port (code block 3 below) works. For options 1 and 2, I receive the following error in the frontend socket.onerror: {"message": "Received bad response code from server: 404."}

1. const wss = new WebSocket.Server({ server }) 2. const wss = new WebSocket.Server({ noServer: true }) 3. const wss = new WebSocket.Server({ port: 5002 })

What I've tried:

  1. I attempted to test the upgrade using core modules following the instructions provided in this issue, but encountered a problem where the upgrade is not triggered as expected. 'It works' is not printed.
    
    const crypto = require('crypto');
    const http = require('http');

const request = http.get({ headers: { 'Sec-WebSocket-Key': crypto.randomBytes(16).toString('base64'), 'Sec-WebSocket-Version': 13, 'Connection': 'Upgrade', 'Upgrade': 'websocket' }, hostname: 'local_ip_address', port: '5001' });

request.on('upgrade', function () { console.log('It works.'); });


### ws version

^8.17.0

### Node.js Version

v18.12.1

### System

OS: macOS 14.5
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 51.72 MB / 16.00 GB
Shell: 5.9 - /bin/zsh

### Expected result

- I expected the WebSocket server to connect successfully using the existing HTTP server created with Koa.
`const wss = new WebSocket.Server({ server });` 

- I expected the WebSocket server to handle the upgrade correctly.

const wss = new WebSocket.Server({ noServer: true });

server.on("upgrade", (request, socket, head) => { wss.handleUpgrade(request, socket, head, (ws) => { wss.emit("connection", ws, request); }); });



### Actual result
`Error: Unexpected server response: 404`

### Attachments

_No response_
lpinca commented 1 month ago

I attempted to test the upgrade using core modules following the instructions provided in https://github.com/websockets/ws/issues/1343#issuecomment-377969496, but encountered a problem where the upgrade is not triggered as expected.

It means that the request is not hitting the server. Is the server listening? Is there a proxy in the middle? I have no clue.

ru-zxr commented 1 month ago

I attempted to test the upgrade using core modules following the instructions provided in #1343 (comment), but encountered a problem where the upgrade is not triggered as expected.

It means that the request is not hitting the server. Is the server listening? Is there a proxy in the middle? I have no clue.

I created a create-react-app to test all three options, and all function correctly. However, when I use it in a React Native Expo app, only when specifying a different port, the message 'It works' is printed.

The socket client upgrade script prints: Error making request: Error: connect ECONNREFUSED local_ip_address:80 when usingnew WebSocket.Server({ noServer: true }) and new WebSocket.Server({ server })

lpinca commented 1 month ago

Is the server listening on port 80? You might need elevated privileges to make the server listen on port 80.

ru-zxr commented 1 month ago

Is the server listening on port 80? You might need elevated privileges to make the server listen on port 80.

I need to specify the port as 80: const wss = new WebSocket.Server({ port: 80 }). This still creates another TCP server.

lpinca commented 1 month ago

I don't think it is a ws issue. Both of the examples below work as expected.

const http = require('http');
const WebSocket = require('ws');

const server = http.createServer();

const wss = new WebSocket.Server({ server });

wss.on('connection', function (ws) {
  ws.send('Hello');
});

server.listen(80, function () {
  const ws = new WebSocket('ws://127.0.0.1');

  ws.on('open', function () {
    console.log('Open');
  });

  ws.on('message', function (buf) {
    console.log(buf.toString());
  });
});
const http = require('http');
const WebSocket = require('ws');

const server = http.createServer();

const wss = new WebSocket.Server({ noServer: true });

wss.on('connection', function (ws) {
  ws.send('Hello');
});

server.on('upgrade', function (request, socket, head) {
  wss.handleUpgrade(request, socket, head, function (ws) {
    wss.emit('connection', ws);
  });
});

server.listen(80, function () {
  const ws = new WebSocket('ws://127.0.0.1');

  ws.on('open', function () {
    console.log('Open');
  });

  ws.on('message', function (buf) {
    console.log(buf.toString());
  });
});
ru-zxr commented 1 month ago

It was my mistake. I was using app.listen() instead of server.listen(). The app.listen() method creates and starts a new HTTP server, which does not have the WebSocket server attached to it. Thanks for the support.