Search code examples
reactjsdockerwebsocketproxyhttp-proxy-middleware

Websockets not working with http-proxy-middleware


I'm trying to configure websockets through http-proxy-middleware. I'm using Docker with NodeJS and React with react-create-app.

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use('/api/v4/', createProxyMiddleware({ target: 'http://drf:8081', changeOrigin: true}));
  app.use('/api/v4/ws/users/', createProxyMiddleware({ target: 'ws://asgi-server:8082', changeOrigin: true, ws: true}));
};

socket-service.ts

import React from 'react';

interface Props {
  cookies: any;
  page: any;
  address: any;
}

interface requestFormatRPC {
  method: string;
  params: {[key: string]: any};
  id: string;
}

interface responseFormatRPC {
  result: any;
  error: any;
  id: string;
}

export const socketService = (data: Props) => {
  const wsProtocol = window.location.protocol === 'http:' ? 'ws:' : 'wss:';
  const webSocket = new WebSocket(
    `${wsProtocol}//${window.location.host}/api/v4/ws/users/`
  );
};

export class webSocketService {
  webSocket: any = null;
  subscribers: any = {};
  sessionId: any = null;

  constructor() {
    //...
  }

  private addListeners(self = this) {
    console.log('ADD LISTENERS START');
    this.webSocket.onmessage = function (event: { [key: string]: any }) {
      const data = JSON.parse(event.data);
      self.notifySubscribers(data.event, data.data);
    };

    this.webSocket.onopen = function () {
      let sessionCookie = document.cookie.replace(
        /(?:(?:^|.*;\s*)session_id\s*\=\s*([^;]*).*$)|^.*$/,
        '$1'
      );
      self.sessionId = sessionCookie ? sessionCookie.split('|')[4].split(':')[1] : null;
      console.log(self.sessionId);
      self.notifySubscribers('onopen', {});
    };
  }

  private notifySubscribers(event: string, data: any) {
    const list = this.subscribers[event];
    if (list) {
      list.forEach((el: any) => {
        el.callback(event, data);
      });
    }
  }

  connect() {
    if (!this.webSocket) {
      console.log('CONNECT WS START');
      const wsProtocol = window.location.protocol === 'http:' ? 'ws:' : 'wss:';
      this.webSocket = new WebSocket(
        `${wsProtocol}//${window.location.host}/api/v4/ws/users/`
      );
      this.addListeners();
    }
  }

  subscribe(events: any[], callback: any) {
    if (events && events.length && callback) {
      events.forEach((event: any) => {
        if (!this.subscribers[event]) {
          this.subscribers[event] = [];
        }
        this.subscribers[event].push({
          event: event,
          callback: callback,
        });
      });
    }
  }

  send(event: any, data: any) {
    if (event && data && this.sessionId) {
      this.webSocket.send(
        JSON.stringify({ event, data, session: this.sessionId })
      );
    }
  }
}

But it only works for my api drf. For websockets Google and Firefox sends error: WebSocket connection to 'ws://localhost:7000/api/v4/ws/users/' failed:

Google Error

I tried to add WDS_SOCKET_PORT=0 in env and docker-compose.yml, but nothing helps I set it up with Nginx for production, but i have no understanding how to do it locally with React


Solution

  • I just figured it out by editing http-proxy-middleware

    const { createProxyMiddleware } = require('http-proxy-middleware');
    
    module.exports = function(app) {
      app.use(
        '/api/v4/',
        createProxyMiddleware({
          target: 'http://drf:8081',
          changeOrigin: true,
        })
      );
      app.use(
        createProxyMiddleware('/api/v4/ws/', {
          target: 'http://asgi-server:8082',
          changeOrigin: true,
          ws: true,
        })
      );
    };
    

    It's a bit different and i do not understand the changes, but it works. I find the solution here Link