Matheus Sartori
Matheus Sartori

Reputation: 189

WebSockets via browser on React + NodeJS

I'm building a websocket chat service, running on NodeJS. It's already working on terminal, if you want to try, check the terminal client package. https://www.npmjs.com/package/@redstone-solutions/hackerchat-client

It works fine on the terminal, now i'm developing a package to integrate web apps (javascript, react, etc), but i can't connect to the websocket via browser.

Basically, that's the backend on the websocket creation: (I'm not using socket.io, the only dependency is uuid)

async initialize(eventEmitter: NodeJS.EventEmitter): Promise<http.Server> {
    const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {
      response.setHeader('Access-Control-Allow-Origin', '*');
      response.setHeader('Access-Control-Request-Method', '*');
      response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
      response.setHeader('Access-Control-Allow-Headers', '*');
      
      response.writeHead(200, { 'Content-Type': 'text/plain' })
      response.end('Hacker chat server is running!\n\nPlease connect with websocket protocol.')
    })

    server.on('upgrade', (request, socket) => {
      socket.id = uuid()
      const headers = [
        'HTTP/1.1 101 Web Socket Protocol Handshake',
        'Upgrade: WebSocket',
        'Connection: Upgrade',
        ''
      ]
        .map(line => line.concat('\r\n'))
        .join('')

      socket.write(headers)
      eventEmitter.emit(EventTypes.event.NEW_USER_CONNECTED, socket)
    })

    return new Promise((resolve, reject) => {
      server.on('error', reject)
      server.listen(this.port, () => resolve(server))
    })
  }

Here's my try on a new react app, i'm using the same ways that i use on the terminal client.

function App() {
  useEffect(() => {
    async function run() {
      const componentEmitter = new Events()

      const connection = await createConnection()
      console.log(connection)
    }

    run()
  }, [])

  async function createConnection() {
    const options = {
      port: '9898',
      host: 'localhost',
      headers: {
        Connection: 'Upgrade',
        Upgrade: 'websocket'
      }
    }

    const request = http.request(options)
    request.end()

    return new Promise(resolve => {
      request.once('upgrade', (response, socket) => resolve(socket))
    })
  }

  return <div />
}

The request never upgrades on the react app! The console.log(connection) is never called.

I'm not sure if i'm using a resource that's not compatible with browsers (http package, for example)...

The applications are very simple/small, if you need the full code:

https://github.com/redstone-solutions/hackerchat-server

https://github.com/redstone-solutions/hackerchat-terminal-client

Any idea?

Upvotes: 0

Views: 566

Answers (1)

Matheus Sartori
Matheus Sartori

Reputation: 189

I found a way to connect with the socket, but I will need to restructure my application.

const server = new WebSocket('ws://localhost:9898')

Using the native WebSocket API from JavaScript, i can stablish a connection.

With the http library, the request never upgrades, i believe that this lib doesn't fully work on the browser.

Upvotes: 0

Related Questions