Простой Http-сервер с использованием модуля Nodejs Net

В этом посте мы создадим HTTP-сервер с помощью модуля Nodejs net, который поможет вам понять основы HTTP. Сервер будет очень простым и будет обслуживать html-файлы в каталоге.

Сначала создадим tcp-сервер, который будет приветствовать клиентов, используя модуль net, чтобы мы могли понять, как работает HTTP.

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');
});
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь мы создали сервер, который слушает PORT 9090. После того как клиент устанавливает соединение, он печатает client connected. Когда клиент отправляет данные, он печатает их и отправляет hello клиенту. После этого socket.end(); закрывает соединение.

Давайте попробуем с netcat. Мы будем использовать netcat в качестве TCP-клиента. Если у вас нет netcat, вы можете использовать что угодно.

Запустите сервер. Откройте новую вкладку терминала для netcat. nc localhost 9090 создаст соединение, и вы сразу увидите сообщение client connected на сервере. Перейдите на вкладку терминала netcat и напишите TEST и нажмите Enter. Вы увидите сообщение hello, пришедшее с сервера. Каждый раз, когда вы отправляете сообщение с клиента на сервер, вы будете видеть сообщение hello.

Теперь перейдите на вкладку терминала сервера и вы должны увидеть сообщение, которое вы отправили с клиента.

Давайте проверим это на браузере. Вы можете использовать ваш браузер по умолчанию, но я буду использовать safari. Откройте localhost:9090 в браузере.

Мы видим, что клиент установил соединение и отправил некоторые данные, но в браузере мы видим ошибку can not connect to server.

Почему 🤔. Потому что сообщение, которое возвращает сервер, еще не является действительным HTTP-ответом. Мы должны применить протокол передачи гипертекста (HTTP), чтобы браузер мог понять сообщение и отобразить его.

По сути, HTTP говорит следующее;

сообщение состоит из начальной строки, нуля или более полей заголовков (также известных как «заголовки»), пустой строки (т.е. строки, в которой ничего не предшествует CRLF), указывающей на конец полей заголовков, и, возможно, тела сообщения.

Дополнительная информация

Итак, наше ответное сообщение должно выглядеть следующим образом:

  1. Строка состояния. Она состоит из трех элементов:
    • Номер версии HTTP. HTTP/1.1.
    • Код состояния. 200
    • Фраза причины. OK
  2. Заголовки
  3. Тело
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 OKn';
    socket.write(statusLine);

    const header = 'Server: SimpleHTTPServer NodejsnContent-type: text/htmlnn';
    socket.write(header);

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

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

server.listen(9090, () => {
  console.log('Listening 9090');
});
Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что есть один n в конце statusLine и между заголовками (Server:, Content-type:), но есть два n после заголовков. Это указывает на то, что начинается тело.

Давайте еще раз откроем localhost:9090 в браузере.

Тадаа 🎉.

Подача HTML-файлов

Теперь мы можем создать программу, которая будет обслуживать HTML файлы. Она будет понимать, какой HTML файл хочет получить клиент из заголовка запроса. Например, если клиент обращается к localhost:9090/contact, мы должны увидеть GET /contact HTTP/1.1 в заголовке запроса. Поэтому мы разберем заголовок запроса, попытаемся найти этот файл (например, contact.html) и ответим клиенту. Если клиент перейдет на домашнюю страницу, мы ответим index.html.

Как и HTTP-ответ, HTTP-запрос следует тем же правилам. За каждым HTTP-заголовком следует строка возврата каретки (CRLF). После последнего из HTTP-заголовков используется дополнительный CRLF (для получения пустой строки), а затем начинается тело сообщения.

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

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

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

    const header = 'HTTP/1.1 200 OKnServer: SimpleHTTPServer Nodejsnn';
    socket.write(header);

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

    socket.end();
  });
Вход в полноэкранный режим Выход из полноэкранного режима

data.toString().split('nn') разделяет заголовок и тело запроса. Мы получаем первый элемент массива после разделения (который является заголовком) и присваиваем его requestHeader.

Мы знаем, что заголовки делятся на новые строки. .split('n') разделяет все заголовки и помещает каждый заголовок в массив, но мы получаем только первый заголовок, который сообщает нам метод, путь и версию (GET /contact HTTP/1.1) и присваиваем его requestLine.

Мы разделяем requestLine на пустые пробелы и получаем method, path, httpVersion.

Наконец, мы возвращаем эти значения клиенту. Если вы перейдете в браузер, то увидите на экране GET /contact HTTP/1.1.

Теперь давайте создадим два HTML-файла, index.html и contact.html. Вы можете поместить в них все, что захотите!

Перед слушателем соединения создайте новую функцию handleRequest. Она будет получать путь в качестве параметра.

const handleRequest = async (path) => {
  let requestedFile = `${path}.html`;
  if (path === '/') {
    requestedFile = '/index.html';
  }
  const fileData = await fs.promises.readFile(`.${requestedFile}`);
  return fileData;
};
Вход в полноэкранный режим Выход из полноэкранного режима

Если клиент запросил домашнюю страницу, то запрашиваемый файл будет index.html, если нет, то запрашиваемый файл будет путь (например: localhost:9090/contact). Он прочитает запрошенный файл и передаст данные в fileData с помощью обещаний (Мы также можем использовать .pipe() вместо обещаний).

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

const header = 'HTTP/1.1 200 OKnServer: SimpleHTTPServer Nodejsnn';
socket.write(header);

socket.write(fileData);
socket.end();
//...
Вход в полноэкранный режим Выход из полноэкранного режима

Мы отправляем данные HTML файла, которые вернулись из handleRequest с помощью socket.write клиенту.

Чтобы лучше понять, мы можем снова использовать netcat. Откройте терминал. Пока сервер работает, напишите nc localhost 9090. Это приведет к подключению к серверу. Теперь мы должны отправить сообщение на сервер. Нам нужен файл contact.html. Поэтому если мы отправим в заголовке путь /contact, сервер должен ответить файлом contact.html. Для этого напишите GET /contact HTTP/1.1rn.

Теперь вы создали простой HTTP-сервер с помощью модуля nodejs net.

Спасибо за прочтение. Вы можете найти исходный код на Github

Оцените статью
Procodings.ru
Добавить комментарий