socket.iobrowser-syncaureliagulp-browser-sync

How to fix browser-sync & socket.io conflict?


I seem to have a conflict of the socket used in browser-sync and the one I want to use with socket.io. I use browser-sync via a gulp task (that comes with Aurelia CLI default project). What happens is that only the browser-sync socket seems to work and the socket.io that is mostly used on server side is completely unread. I did find the GitHub issue #71 which is addressing the problem, and also this other Github issue #241 which both mention the use of namespace. However I tried a few variances and even if they run with errors, it doesn't fix any of my problems.

I mainly use Gulp with browser-sync inside an Aurelia CLI project, while the backend is NodeJS with Koa. The version of socket.io@1.6.0 while browser-sync@2.13.0 The ports used are for frontend: 4000, backend: 5000.

Here's a list of the files

Gulp - run.js

import gulp from 'gulp';
import browserSync from 'browser-sync';
import historyApiFallback from 'connect-history-api-fallback/lib';
import project from '../aurelia.json';
import build from './build';
import {CLIOptions} from 'aurelia-cli';
import url from 'url';
import proxy from 'proxy-middleware';

var portBackEnd = 5000;
var portFrontEnd = 4000;

var proxyOptionsAccessControl = function(req,res, next){
      res.setHeader('Access-Control-Allow-Origin', '*');
      next();
};

var proxyOptionsApiRoute = url.parse(`http://localhost:${portBackEnd}/api`);
    proxyOptionsApiRoute.route = '/api';

var proxyOptionsAuthRoute = url.parse(`http://localhost:${portBackEnd}/auth`);
    proxyOptionsAuthRoute.route = '/auth';

function log(message) {
  console.log(message); //eslint-disable-line no-console
}

function onChange(path) {
  log(`File Changed: ${path}`);
}

function reload(done) {
  browserSync.reload();
  done();
}

let serve = gulp.series(
  build,
  done => {
    browserSync({
      online: false,
      open: false,
      port: portFrontEnd,
      notify: true,
      logLevel: 'silent',
      server: {
        baseDir: ['.'],
        middleware: [
          proxyOptionsAccessControl,
          proxy(proxyOptionsApiRoute),
          proxy(proxyOptionsAuthRoute)
        ]
      },
      socket: {
        domain: 'localhost:4000',
        namespace: '/browsersync'
      }
    }, function(err, bs) {
      let urls = bs.options.get('urls').toJS();
      log(`Application Available At: ${urls.local}`);
      log(`BrowserSync Available At: ${urls.ui}`);
      done();
    });
  }
);

let refresh = gulp.series(
  build,
  reload
);

let watch = function() {
  gulp.watch(project.transpiler.source, refresh).on('change', onChange);
  gulp.watch(project.markupProcessor.source, refresh).on('change', onChange);
  gulp.watch(project.cssProcessor.source, refresh).on('change', onChange);
};

let run;

if (CLIOptions.hasFlag('watch')) {
  run = gulp.series(
    serve,
    watch
  );
} else {
  run = serve;
}

export default run;

use of Socket.io-Client with Aurelia (frontend)

import io from 'socket.io-client';

var socket = io('http://localhost:5000');
// also tried with a namespace instead
//var socket = io('/todo-socket');

// ...

socket.on("todo_update", data => {
  let pos = arrayFindObjectIndex(this.items, 'id', data.id);
  if(pos >= 0) {
     this.items.splice(pos, 1, data);
     this.itemDoneCount = this.items.filter(x => x.completed).length;
   }
});

NodeJS with Koa (backend)

// Middlewares
const app = require('koa')();
const serve = require('koa-static');
const api = require('koa-router')();
const assertTimeout = require('co-assert-timeout');
const http = require('http');
const server = require('http').createServer(app.callback());
const io = require('socket.io')(server);

// or without namespace: io.sockets.on("connection", function(socket) {
var tsp = io.of('/todo-socket');
tsp.on("connection", function(socket) {
  console.log('A new WebSocket client connected with ID: ' + socket.client.id);
})
.error(function(err){
  console.log("Changefeeds Failure: ", err);
  return null;
});

socket.on('disconnect', function(data) {
  console.log('A WebSocket client disconnnected with ID: ' +      socket.client.id);
  return null;
});

I tried with and without namespace, that doesn't seem to change anything. I referred to browser-sync - socket option

EDIT

From testing this more, it actually seems like the socket.io on server side isn't working at all. It doesn't output the console.log('connected with ID: ' + socket.client.id) from the server, however it doesn't display or throw any errors either. The code on server side used to work with WebPack, so I really think it's related to browserSync conflict.


Solution

  • After passing couple days on it, I got it working. I had to change the namespace at 2 different places, in the browserSync options and also in the client (Aurelia) where I use the other socket which has to be a different namespace. Then finally, the NodeJS Koa has to be called with the io.of('/clientNamespace').

    An important note, be aware that both the browserSync and the client namespace must be declared with the full URL:port+namespace (ex.: http://localhost:4000/namespaceBs), but on the server side, the namespace is only used with the name without the URL (ex.: /namespaceClient).

    If you ever wonder, I called it todoSocket because it's a TODO App

    So to make it short, I changed the following code:

    Gulp - run.js

    let serve = gulp.series(
      build,
      done => {
        browserSync({
          online: false,
          open: false,
          port: portFrontEnd,
          notify: true,
          logLevel: 'silent',
          server: {
            baseDir: ['.'],
            middleware: [
              proxyOptionsAccessControl,
              proxy(proxyOptionsApiRoute),
              proxy(proxyOptionsAuthRoute)
            ]
          },
          socket: {
            namespace: `http://localhost:4000/bs` // <<<< HERE >>>>
          }
        }, function(err, bs) {
          let urls = bs.options.get('urls').toJS();
          log(`Application Available At: ${urls.local}`);
          log(`BrowserSync Available At: ${urls.ui}`);
          done();
        });
      }
    );
    

    use of Socket.io-Client with Aurelia (frontend)

    import io from 'socket.io-client';
    
    var socket = io('http://localhost:5000/todo-socket'); // <<<< HERE >>>>
    

    NodeJS with Koa

    const http = require('http');
    const server = http.createServer(app.callback());
    const io = require('socket.io')(server);
    
    // Load config for RethinkDB and koa
    const config = require("./config");
    
    // attach on the todo-socket namespace, only the name (not the full URL)
    var todoSocket = io.of('/todo-socket');         // <<<< HERE >>>>
    todoSocket.on("connection", function(socket) {  // <<<< AND HERE >>>>
      console.log('A new TODO WebSocket namespace client connected with ID: ' + socket.client.id);
    });