Hand-on instructions for implementing HTTP protocol with Node

Hand-on instructions for implementing HTTP protocol with Node (3)

Previous Chapter Describes how to parse HTTP request messages. In this chapter, we will explain how to send and receive messages and how to establish a TCP connection.

TCP is a full duplex communication channel. We can create a TCP process by using Node's net module to listen for requests from clients, using the following methods:

net.createServer((socket) => {
  socket.on('data', (data: Buffer) => {
    // ...Processing received client information
  });

  socket.on('error', error => {
    // ...Processing error information
  });
});

The data received in the data event belongs to the byte stream data (Buffer object), and we need to parse the byte stream using the toString method that comes with the Buffer object to convert it to utf-8 characters.

The converted string is then handled by our HttpParser, and the serialized request object is mounted on req in http.createServer ((req, res) => {}) with message response via res.end(message).

By parsing the steps above, we know that we need two more classes, one to hold Request information and one to handle Response for response results, so now we will create two new classes:

// Used to process request information
import HttpParser, { HttpMessage } from "./HttpParser";

class IncomingMessage {
  private httpParser: HttpParser;
  public httpMessage: HttpMessage;

  constructor(message: string) {
    this.httpParser = new HttpParser(message);
    this.httpMessage = this.httpParser.httpMessage;
  }
}

export default IncomingMessage;
// Used for response processing results
import * as net from 'net';
// ResponseFormatter is the class that deserializes JSON data and can be viewed from the source repository
import ResponseFormatter from './ResponseFormatter';

class ServerResponse {
  private socket: net.Socket;
  private resFormatter: ResponseFormatter;
  
  constructor(socket: net.Socket) {
    this.socket = socket;
    this.resFormatter = new ResponseFormatter();
  }

  public setHeader(key: string, val: string) {
    this.resFormatter.setHeader(key, val);
  }
  
  public end(status: number, body: string) {
    const resFormatter = this.resFormatter;
    resFormatter.setStatus(status);
    resFormatter.setBody(body);
    // The next three steps are to send TCP byte stream data to the client
    this.socket.write(resFormatter.format());
    this.socket.pipe(this.socket);
    this.socket.end();
  }
}

export default ServerResponse;

Finally, we add these two objects to our event monitoring:

socket.on('data', (data: Buffer) => {
  // ...Processing received client information
  const message = data.toString('utf-8'); // Decode Byte Stream Data
  this.request = new IncomingMessage(message); // Encapsulate request object
  this.response = new ServerResponse(socket); // Encapsulate response object
  this.handler(this.request, this.response); // Pass two objects as parameters into the callback function
});

Now all we need to do is add the callback function to the HTTP object correctly, and our final implementation of the HTTP class is as follows:

import * as net from 'net';
import * as EventEmitter from 'events';
import IncomingMessage from "./IncomingMessage";
import ServerResponse from "./ServerResponse";

type Handler = (request: IncomingMessage, response: ServerResponse) => void;

class HTTP extends EventEmitter{
  handler: Handler;
  request: IncomingMessage;
  response: ServerResponse;
  server: net.Server;
  socket: net.Socket;

  constructor(handler: Handler) {
    super();
    this.handler = handler;
    this.createServer();
  }

  private createServer(): void {
    this.server = net.createServer((socket) => {
      socket.on('data', (data: Buffer) => {
        const message = data.toString('utf-8');
        this.request = new IncomingMessage(message);
        this.response = new ServerResponse(socket)
        this.handler(this.request, this.response);
      });

      socket.on('error', error => {
        this.emit('error', error)
      });
    });
  }

  public listen(port: number, cb: any = () => { }): void {
    this.server.listen(port, cb);
    this.server.on('error', error => this.emit('error', error));
  }
}

const createServer = (handler: Handler) => {
  return new HTTP(handler)
}

export default {
  createServer
}

After completing the final HTTP class, we can create an HTTP server through the method of Chapter 1, and can process the request information and return it to the client as many as possible.

So far, an HTTP protocol has been implemented!

Source address, welcome to Star

Original address, welcome to Star

Tags: node.js socket JSON

Posted on Sat, 09 Nov 2019 03:10:51 -0500 by sansoo