DEV Community

Osman Mert Acar
Osman Mert Acar

Posted on

Simple Http Server Using Nodejs Net Module

In this post we will build a HTTP Server using Nodejs net module which will help you understand HTTP basics. The server will be very simple and serve html files in a directory.

First, let's create a tcp server that greets clients using net module so that we can understand how HTTP works.

const net = require('net');

const server = net.createServer();

server.on('connection', (socket) => {
  console.log('client connected');

  socket.on('data', (data) => {
    console.log(data.toString());
    socket.write('hello');
    socket.end();
  });
});

server.listen(9090, () => {
  console.log('Listening 9090');
});
Enter fullscreen mode Exit fullscreen mode

Here we created a server that listens PORT 9090. After the client makes a connection, it prints client connected. When the client sends data, it prints the data and sends hello to client. When it is done socket.end(); closes the connection.

Let's try with netcat. We will use netcat as a TCP client. If you do not have netcat you can use anything you want.

Run the server. Open up new terminal tab for netcat. nc localhost 9090 will create a connection and you will immediately see client connected message on the server. Go to netcat terminal tab and write TEST and press enter. You will see hello message that coming from server. Anytime you send a message from client to server you will see hello message.

Client

Now, go to server terminal tab and you should see the message you sent from client.

Server

Let's test this on a browser. You can use your default browser but I will use safari. Open localhost:9090 on the browser.

Browser request

We see client made a connection and sent some data but on the browser we see can not connect to server error.

Browser can not connect to server

Why 🤔. Because the message that server is returning is not a valid HTTP response yet. We should apply Hypertext Transfer Protocol (HTTP) so browser can understand the message and render it.

Basically, what HTTP saying is;

message consist of a start-line, zero or more header fields (also known as "headers"), an empty line (i.e., a line with nothing preceding the CRLF) indicating the end of the header fields, and possibly a message-body.

More info

So our response message should look like this:

  1. Status line. It consists of three items:
    • HTTP version number. HTTP/1.1
    • Status code. 200
    • Reason phrase. OK
  2. Headers
  3. Body
const net = require('net');

const server = net.createServer();

server.on('connection', (socket) => {
  console.log('client connected');

  socket.on('data', (data) => {
    console.log(data.toString());
    const statusLine = 'HTTP/1.1 200 OK\n';
    socket.write(statusLine);

    const header = 'Server: SimpleHTTPServer Nodejs\nContent-type: text/html\n\n';
    socket.write(header);

    const body = 'Hello World';
    socket.write(body);

    socket.end();
  });
});

server.listen(9090, () => {
  console.log('Listening 9090');
});
Enter fullscreen mode Exit fullscreen mode

Notice that there is one \n at the end of the statusLine and between the headers (Server:, Content-type:), but there are two \n after the headers. This indicates that the body begins.

Let's open localhost:9090 on a browser one more time.

Successful HTTP Response

Tadaa 🎉.

Serve HTML Files

Now we can create a program that will serve HTML files. It will understand which HTML file client wants from request header. For example if the client goes to localhost:9090/contact, we should see GET /contact HTTP/1.1 in the request header. So we will parse the request header, try to find that file (eg contact.html) and respond to client. If client goes to the homepage we will respond with index.html.

Like HTTP response, HTTP request follows the same rules. Each HTTP header is followed by a carriage return line feed (CRLF). After the last of the HTTP headers, an additional CRLF is used (to give an empty line), and then any message body begins.

socket.on('data', (data) => {
    const [requestHeader] = data.toString().split('\n\n');

    const [requestLine] = requestHeader.split('\n');

    const [method, path, httpVersion] = requestLine.split(' ');

    const header = 'HTTP/1.1 200 OK\nServer: SimpleHTTPServer Nodejs\n\n';
    socket.write(header);

    const body = `${method} ${path} ${httpVersion}`;
    socket.write(body);

    socket.end();
  });
Enter fullscreen mode Exit fullscreen mode

data.toString().split('\n\n') splits the header and the body from request. We get first element of array after split (which is the header) and assign it to requestHeader

We know that headers are divided by new lines. .split('\n')splits all headers and put each header into an array, but we only get the first header which tells us the method, path and version (GET /contact HTTP/1.1) assign it to requestLine.

We are splitting requestLine by empty spaces, and getting method, path, httpVersion

Lastly we are returning those values to client. If you go to the browser you should see GET /contact HTTP/1.1 on the screen.

Now, let's create two HTML files, index.html and contact.html. You can put anything you want in them!

Before connection listener, create a new function called handleRequest. It will get the path as a parameter.

const handleRequest = async (path) => {
  let requestedFile = `${path}.html`;
  if (path === '/') {
    requestedFile = '/index.html';
  }
  const fileData = await fs.promises.readFile(`.${requestedFile}`);
  return fileData;
};
Enter fullscreen mode Exit fullscreen mode

If client requested homepage, requested file is index.html, if not, requested file is the path (eg: localhost:9090/contact). It will read the requested file and assign data to fileData with promises (We could also use .pipe() instead promises).

// ...
const fileData = await handleRequest(path);

const header = 'HTTP/1.1 200 OK\nServer: SimpleHTTPServer Nodejs\n\n';
socket.write(header);

socket.write(fileData);
socket.end();
//...
Enter fullscreen mode Exit fullscreen mode

We are sending the HTML file data that returned from handleRequest with socket.write to client.

Homepage

To understand better, we can use netcat again. Open a terminal. While server is running, write nc localhost 9090. This will connect to the server. Now, we should send a message to the server. We want the contact.html file. So if we send /contact path in header, the server should respond with contact.html. To do that, write GET /contact HTTP/1.1\r\n.

netcat http request

Now you have built a simple HTTP server using nodejs net module.

Thank you for reading. You can find the source code on Github

Latest comments (0)