realtebo
realtebo

Reputation: 25641

Does socket.io server need to be attached to an http server?

I'm studying for the first time socket.io. I'd like to start a simple socket server. I see every example I found in the internet made like this:

var http = require('http');
var fs = require('fs');

// Loading the index file . html displayed to the client
var server = http.createServer(function(req, res) {
    fs.readFile('./index.html', 'utf-8', function(error, content) {
        res.writeHead(200, {"Content-Type": "text/html"});
        res.end(content);
    });
});

// Loading socket.io
var io = require('socket.io').listen(server);

// When a client connects, we note it in the console
io.sockets.on('connection', function (socket) {
    console.log('A client is connected!');
});


server.listen(8080);

Question

The core of my question is simple: in all examples like this, I see io to listen attached to an existing http server.

Is this a requirement? If yes, why? Is websocket protocol transported via http?

Use case

My use case is a bit strange probably: I've a vue dev server that must be kept running because it's offering live reload, etc.. It's listening on port 8080 and it's offered by vue-cli-service so I cannot alter it to attach something.

Also, I've a 'hand-made' and experimental smtp server, made in node.js and running on port 25

I want to use websocket to allow my smtp server to 'post' and my Vue.js webapp to 'listen' .

Final goal: creating a standalone smtp-catch-all system with a simple webgui, so every mail sent via my smtp is 'pushed' into my webapp and never stored (and never really sent !)

Given this, I'd like to create a 'standalone' socket.io server. If it simply needs an http server, I create a second one on a specific port to allow http->websocket upgrade; then my web app will simply comunicate with the socket server on this secondary server.

I prefer in this way, because, using pm2, I can run each service without touch the other

Upvotes: 0

Views: 928

Answers (2)

realtebo
realtebo

Reputation: 25641

I add a proof-of-concept about an smtp server that simply catch all received mail but do not send to real target.

Also, it emits events on a socket.io connection.

All without using an http server.

SMTP + socket.js server

const SMTPServer = require("smtp-server").SMTPServer;

const io = require('socket.io')(2626);

const server = new SMTPServer({
  logger: true,
  debug: true,
  allowInsecureAuth: true,
  onAuth(auth, session, callback) {
    console.log("onAuth, user", auth.username, "password", auth.password);
    if (auth.username !== "abc" || auth.password !== "def") {
      return callback(new Error("Invalid username or password"));
    }
    io.emit("smtpAuth", auth);
    callback(null, { user: 123 }); // where 123 is the user id or similar property
  },

  onConnect(session, callback) {
    console.log("onConnect, from ip ", session.remoteAddress);
    io.emit("smtpConnect", session);
    return callback(); // Accept the connection
  },

  onMailFrom(address, session, callback) {
    console.log("onMailFrom", address.address);
    io.emit("smtpMailFrom", address);
    return callback(); // Accept the address
  },

  onRcptTo(address, session, callback) {
    console.log("onRcptTo", address.address);
    io.emit("smtpRcptTo", address);
    return callback(); // Accept the address
  },

  onData(stream, session, callback) {
    var email_content = "";
    // stream.pipe(email_content); // print message to console
    stream.on('data', (chunk) => { email_content += chunk });
    stream.on("end", function() {
      // Stream ended
      console.log (email_content);
      io.emit("smtpData", email_content);
      callback();
    });
  }
});

server.on("error", err => {
  console.log("Error %s", err.message);
});

server.listen(25);

VUE.JS example code

This component is automatically informed by socket.io server if something new is happening. In this case, it can log all events but also save entire email (raw) data.

<template>
  <div class="hello">
    <h1>Le email che abbiamo ricevuto per te</h1>
    <pre 
      v-for="(message, index) in messages" 
      :key="index"
    >{{ 
      message
    }}</pre>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      messages: [],
    }
  },
  sockets: {
    connect() {
      console.log('socket connected')
    },
    smtpAuth(val) {
      console.log('SMTP AUTH', val);
    },
    smtpConnect(val) {
      console.log('SMTP CONNECT', val);
    },
    smtpMailFrom(val) {
      console.log('SMTP MAIL FROM', val);
    },
    smtpRcptTo(val) {
      console.log('SMTP RCPT TO', val);
    },
    smtpData(val) {
      console.log('SMYP DATA', val);
      this.messages.push(val);
    }
  },
};
</script>

Example of how to keep running smtp/socket server and a vue-cli dev server app using pm2

Call this ecosystem.config.js

module.exports = {
  apps: [
    {
      name: "WEB",
      script: "./node_modules/@vue/cli-service/bin/vue-cli-service.js",
      args: "serve"
    },
    {
      watch: true,
      name: "SMTP",
      script: "./server/index.js"
    }
  ]
};

Run it as pm2 run ecosystem.config.js

Upvotes: 0

freakish
freakish

Reputation: 56467

The WebSocket protocol is a separate protocol, it is not built on top of HTTP. However there's an upgrade mechanism (i.e. the WebSocket handshake) in the HTTP protocol itself, that turns a simple HTTP connection into a WebSocket connection. And since browsers use that then it is impossible to establish a WebSocket connection from a browser without HTTP.

Side note: socket.io does utilize other protocols as well. It falls back to them if WebSocket protocol is not available.

Now the following is (obviously) an opinion: there is no need to attach one server to another. In fact this is an anti pattern. You generally want components to be as independent as possible so that changes to one don't affect the other. Moreover: even if both were to run over HTTP there still is no reason to do that. I find those tutorials at least misleading.

Upvotes: 1

Related Questions