How to do it...

The Socket.IO server application will expect the users to be logged-in in order for them to be able to connect to the /home namespace. Using socket middleware, we will also restrict access to /home namespace to a certain user:

  1. Create a new file named middleware-server.js
  2. Include the Socket.IO library and initialize a new HTTP server:
      const http = require('http') 
      const fs = require('fs') 
      const path = require('path') 
      const io = require('socket.io')() 
      const app = http.createServer((req, res) => { 
          if (req.url === '/') { 
              fs.readFile( 
                  path.resolve(__dirname, 'middleware-cli.html'), 
                  (err, data) => { 
                      if (err) { 
                          res.writeHead(500) 
                          return void res.end() 
                      } 
                      res.writeHead(200) 
                      res.end(data) 
                  } 
              ) 
          } else { 
              res.writeHead(403) 
              res.end() 
          } 
      }) 
  1. Specify the path where new connections will be made:
      io.path('/socket.io') 
  1. Define an array of users that we will use as an in-memory database:
      const users = [ 
          { username: 'huangjx', password: 'cfgybhji' }, 
          { username: 'johnstm', password: 'mkonjiuh' }, 
          { username: 'jackson', password: 'qscwdvb' }, 
      ] 
  1. Define a method to verify if the provided username and password exist in the users array:
      const userMatch = (username, password) => ( 
          users.find(user => ( 
              user.username === username && 
              user.password === password 
          )) 
      ) 
  1. Define a namespace middleware function that will check whether the user is already logged-in. A client won't be able to connect to a specific namespace using this middleware if they are not logged in:
      const isUserLoggedIn = (socket, next) => { 
          const { session } = socket.request 
          if (session && session.isLogged) { 
              next() 
          } 
      } 
  1. Define two namespaces, one for /login and another for /home. The /home namespace will use our previously defined middleware function to check whether the user is logged in:
      const namespace = { 
          home: io.of('/home').use(isUserLoggedIn), 
          login: io.of('/login'), 
      } 
  1. When a new socket is connected to /login namespace, first we will define a socket middleware function for checking all incoming packages and ban access to the johntm username. Then, we will add an event listener for the enter event that will expect to receive a plain object containing a username and password, and if they exist in the users array, then we set a session object which will tell whether the user is logged in. Otherwise, we will send a loginError event with an error message to the client:
      namespace.login.on('connection', socket => { 
          socket.use((packet, next) => { 
              const [evtName, data] = packet 
              const user = data 
              if (evtName === 'tryLogin' 
                  && user.username === 'johnstm') { 
                  socket.emit('loginError', { 
                      message: 'Banned user!', 
                  }) 
              } else { 
                  next() 
              } 
          }) 
          socket.on('tryLogin', userData => { 
              const { username, password } = userData 
              const request = socket.request 
              if (userMatch(username, password)) { 
                  request.session = { 
                      isLogged: true, 
                      username, 
                  } 
                  socket.emit('loginSuccess') 
              } else { 
                  socket.emit('loginError', { 
                      message: 'invalid credentials', 
                  }) 
              } 
          }) 
      }) 
  1. Listen on port 1337 for new connections and attach Socket.IO to the HTTP server:
      io.attach(app.listen(1337, () => { 
          console.log( 
              'HTTP Server and Socket.IO running on port 1337' 
          ) 
      })) 
  1. Save the file

After this, build a Socket.IO client application that will connect to our Socket.IO Server and allow us to attempt to log in and test:

  1. Create a new file named middleware-cli.html
  2. Add the following code:
      <!DOCTYPE html> 
      <html lang="en"> 
      <head> 
          <meta charset="UTF-8"> 
          <title>Socket.IO Client</title> 
          <script src="http://localhost:1337/socket.io/socket.io.js">
</script> <script
src="https://unpkg.com/@babel/standalone/babel.min.js">
</script> </head> <body> <h1 id="title"></h1> <form id="loginFrm" disabled> <input type="text" name="username" placeholder="username"/> <input type="password" name="password"
placeholder="password" /> <input type="submit" value="LogIn" /> <output name="logs"></output> </form> <script type="text/babel"> // Code here </script> </body> </html>
  1. Inside the script tag, add the code in the following steps, starting from step 4
  2. Define three constant that will make a reference to the HTML elements that we will use to get input or display output:
      const title = document.getElementById('home') 
      const error = document.getElementsByName('logErrors')[0] 
      const loginForm = document.getElementById('loginForm') 
  1. Define a Socket.IO Manager:
      const manager = new io.Manager( 
          'http://localhost:1337', 
          { path: '/socket.io' }, 
      ) 
  1. Let's define a namespace constant that will contain an object containing the Socket.IO namespaces /home and /login:
      const namespace = { 
          home: manager.socket('/home'), 
          login: manager.socket('/login'), 
      } 
  1. Add an event listener for the connect event to the /home namespace. It will get triggered only when the /home namespace successfully connects to the server:
      namespace.home.on('connect', () => { 
          title.textContent = 'Great! you are connected to /home' 
          error.textContent = '' 
      }) 
  1. Add an event listener for the loginSuccess event to the /login namespace. It will ask the /home namespace to connect to the server again. If the user is logged in, then the server will allow this connection:
      namespace.login.on('loginSuccess', () => { 
          namespace.home.connect() 
      }) 
  1. Add an event listener for the loginError event to the /login namespace. It will display error messages sent by the server:
      namespace.login.on('loginError', (err) => { 
          logs.textContent = err.message 
      }) 
  1. Add an event listener for the submit event for the login form. It will emit the enter event providing an object containing the username and password filled in the form:
      form.addEventListener('submit', (event) => { 
          const body = new FormData(form) 
          namespace.login.emit('tryLogin', { 
              username: body.get('username'), 
              password: body.get('password'), 
          }) 
          event.preventDefault() 
      }) 
  1. Save the file
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.197.212