Перед тем, как бросаться в омут и смотреть на реализацию бэкэнда, нам придётся поговорить
о теоретической базе, скрывающейся за несколькими строчками кода. Небольшого введения
должно хватить для старта, но в будущем нужно будет браться за соответствующие книги
и углубляться в тему. Конечно же, это верно для тех, кто хочет стать хорошим разработчиком.
Пугаться этих книжек не стоит, даже наоборот, после них вы можете почувствовать кураж,
потому что огромное число вещей, которые для вас сейчас представляются черным ящиком,
на поверку окажутся достаточно простыми концепциями с понятным устройством внутри.
Операционная система (ОС)
Основой основ для программиста можно считать ОС. Да, у нас есть еще железо и архитектуру
ЭВМ никто не отменял, но прикладному разработчику достаточно иметь общее понимание того,
как работает железо, а вот от ОС не уйти совсем.
Процесс
Базовой единицей исполнения в ОС является процесс. Каждый раз, когда вы запускаете
какую-либо программу, то запускается минимум один процесс. Кстати, может быть и больше,
отсюда следует что программа != процесс.
Список запущенных процессов можно посмотреть так:
Каждая строчка на рисунке выше – это информация о каком-либо процессе. Как видно,
ОС содержит в себе различную информацию о каждом из процессов. На текущий момент
нас интересует только один параметр, который называется PID. PID, как ни странно,
расшифровывается как process identifier и фактически представляет из себя целое
число, однозначно определяющее то, о каком процессе идет речь.
Сети
Основным (высокоуровневым) способом коммуникации между машинами является семейство
протоколов TCP/IP. Большинство людей, которые пользуются интернетом или
сетями в целом так или иначе слышали выражение ip адрес. Этот адрес (для версии IPv4)
может выглядеть так: 10.0.152.23. Он указывает на какое-то устройство в сети,
которое не обязательно представлено компьютером в привычном понимании этого слова.
Интерфейсы
По правде говоря, этот адрес связан даже не с самим устройством, а с конкретным
интерфейсом устройства. Например, каждая сетевая карта будет представлена в системе
как отдельный интерфейс. Кроме того, интерфейсы бывают виртуальными, то есть
у них отсутствует физический элемент. Зачем это нужно? Самый тривиальный пример
это так называемая обратная петля (loopback). Интерфейс, который присутствует
по умолчанию в большинстве ОС. Любой трафик, посланный в этот интерфейс, тут же
принимается им же.
Этот интерфейс позволяет обращаться к серверному приложению, расположенному на той
же машине, без активного подключения к сети. Такая возможность особенно полезна
для тестирования служб и их разработки. Адрес этого интерфейса всегда 127.0.0.1.
Так же к нему можно обращаться по имени localhost.
Domain Name System
DNS - система доменных имён, благодаря которой нам необходимо запоминать только
буквенные имена сайтов без необходимости знать конкретный ip адрес машины,
на которую надо пойти. Общий принцип работы довольно прост. Каждый раз,
когда в браузер вводится имя сайта, он обращается к специальным серверам
и спрашивает их: 'Какой ip адрес у hexlet.io?'. Дальше происходит немного магии,
и, в конце концов, эта система возвращает (если найдёт) этот адрес. Затем
браузер устанавливает tcp соединение и начинает свою работу.
Порты
Когда происходит общение удаленных машин друг с другом по tcp, то в реальности
между собой общаются процессы ОС, а не компьютеры в целом. Отсюда возникает вопрос:
каким образом, зная только ip адрес, постучаться на чужую машину в интересующую
нас программу. Конкретно в этом курсе нас интересует веб-сервер.
Короткий ответ: никак. И действительно, одного ip адреса недостаточно.
В tcp существует такое понятие, как порт. Это целое число, означающее точку входа
в процесс запущенной программы. То есть при получении данных по tcp ОС смотрит то,
для какого порта они предназначены. Затем она находит процесс, соответствующий этому
порту, и передает данные в него. Важным следствием этого подхода становится тот
факт, что невозможно занять уже занятый порт. Иначе это ввело бы неоднозначность.
И действительно, при старте сервера, который пытается слушать занятый другой программой порт,
будет получена такая ошибка:
Мы можем даже посмотреть на того, кто занял этот порт:
Суммируя вышесказанное, делаем вывод, что любое серверное приложение при старте
должно начать слушать определенный порт для возможности получать данные по сети.
Если вернуться к браузеру, то может возникнуть вопрос: почему мы не указываем порт,
когда загружаем сайты, откуда браузер знает, куда стучаться на сервер? Ответ
на этот вопрос крайне прост: браузер действительно знает порт, на который
нужно идти. И по умолчанию это порт с номером 80.
Веб-сервер
Начнём с иллюстрации:
- Импортируется модуль http. Он встроен в node.js и позволяет создать
веб-сервер (и не только).
- Создаётся веб-сервер. В функцию createServer передается обработчик запросов. Он будет вызываться на каждый входящий запрос.
- Сервер вешается на порт.
Обработки запроса в данном примере как таковой нет. Сервер будет отвечать
по http фразой hello, world! на любой входящий запрос. Делается это
с помощью объекта response, который представляет собой http-ответ.
В примере выше мы используем две функции интерфейса response. Функцию write, которая позволяет передать текст в теле http ответа, и функцию end,
которая означает, что мы закончили формирование ответа. Обратите внимание:
нам не пришлось руками выставлять заголовок content-length, модуль http
самостоятельно вычисляет размер тела и подставляет необходимый заголовок.
Для запуска нашего сервера необходимо набрать команду:
Запуск приводит к блокировке. Сервер запущен и работает. Чтобы его остановить,
надо нажать комбинацию ctrl+c.
Теперь можно выполнить запрос к серверу:
Проделав данную процедуру, можно будет сказать, что вы запустили свой первый сайт
на node.js ;)
Но дальше нас поджидает сюрприз. Если попробовать поменять код сервера, и писать
в ответ my first web server, то без перезапуска сервера ничего не изменится.
После того, как сервер запущен, он больше не перечитывает файлы с диска и не
изменяет своего состояния. Такое поведение не является особенностью модуля http,
так устроен сам язык. Поэтому не забывайте перезапускать сервер, после внесения
изменений.
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 20:52:16 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="eH3e5xySQOGc7JxCpCjhKnRm_xvpkQBDCQEluLJBCL-XrBXQ7uztgSqvuNqoJxFdtG_SseGm_uG04b_s4Ebv0Q";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Реализация сервера | JS: HTTP Server</title>
<meta name="description" content="Реализация сервера / JS: HTTP Server: Изучаем основы, которые помогут понять принципы работы сетевых демонов">
<link rel="canonical" href="https://ru.hexlet.io/courses/js-http-server/lessons/example/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Реализация сервера">
<meta property="og:title" content="JS: HTTP Server">
<meta property="og:description" content="Реализация сервера / JS: HTTP Server: Изучаем основы, которые помогут понять принципы работы сетевых демонов">
<meta property="og:url" content="https://ru.hexlet.io/courses/js-http-server/lessons/example/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="ApPD8IJ5hHGoupGOZbZdbrA6R0Dnf4EHI8NY-lLoGK3tQgjHcAcpER75tRZpua0ZcDNq6u9If6WeI8KuAO__ww" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T20:52:16.457Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"_Q4JhpiqsPGHyG33OYyMvwDEOeW0BbEbsBIucOY6gC0S38KxatQdkTGLSW81g3zIwM0UT7wyT7kN8rQktD1nQw","topics":[{"id":5085,"title":"Зачем передавать порт в функцию solution?","plain_title":"Зачем передавать порт в функцию solution? ","creator":{"public_name":"Denis Smetannikov","id":79938,"is_tutor":false},"comments":[{"creator":{"public_name":"Denis Smetannikov","id":79938,"is_tutor":false},"id":8855,"body":"Дико извиняюсь, разобрался =) Изначально решил, что бинарник уже создает сервер для меня.","topic_id":5085}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":7000,"title":"Проверку проходит вот это \n\n```\nconst server = http.createServer((request, response) => {\n response.end('Welcome to The Phonebook\\nRecords count: 1000');\n });\n console.log(callback());\n server.listen(port, () => {\n console.log('Server has been started');\n });\n```\nоно то и понятно почему проходит, просто мб в условии задачи не надо писать прям 1000 \nWelcome to The Phonebook\n**Records count: 1000**\n\nКоличество записей в phonebook.txt вычисляется в момент чтения файла и используется в выводе Records count: <тут>.\nМб вместо того что выделено сразу записать пояснение","plain_title":"Проверку проходит вот это const server = http.createServer((request, response) => { response.end('Welcome to The Phonebook\\nRecords count: 1000'); }); console.log(callback()); server.listen(port, () => { console.log('Server has been started'); }); оно то и понятно почему проходит, просто мб в условии задачи не надо писать прям 1000 Welcome to The Phonebook Records count: 1000 Количество записей в phonebook.txt вычисляется в момент чтения файла и используется в выводе Records count: <тут>. Мб вместо того что выделено сразу записать пояснение ","creator":{"public_name":"Александр Полуян","id":113917,"is_tutor":false},"comments":[{"creator":{"public_name":"Ruslan Khusnetdinov","id":106784,"is_tutor":false},"id":15147,"body":"Тоже не понял, сначала, что нужно читать файл.\nhttps://ru.hexlet.io/code_reviews/13940?submission_id=17453","topic_id":7000},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":12943,"body":"Спасибо! Чуть улучшил ридми.","topic_id":7000}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":6906,"title":"Мне кажется надо поменять задачу\n```\nПри запросе на / сервер должен отдавать следующее тело:\n\nWelcome to The Phonebook\nRecords count: 1000\n```\nБольше похоже что надо вот этот текст реализовать, а не то сколько реально записей в справочнике&","plain_title":"Мне кажется надо поменять задачу ``` При запросе на / сервер должен отдавать следующее тело: Welcome to The Phonebook Records count: 1000 ``` Больше похоже что надо вот этот текст реализовать, а не то сколько реально записей в справочнике& ","creator":{"public_name":"Dmitrii Pashutskii","id":92375,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":12802,"body":"Спасибо! Поправил.","topic_id":6906}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":20956,"title":"после практики возникли следующие вопросы:\n- для чего тут асинхронный readFile? у меня с синхронным тесты прошли\n- если используюется асинхронный, то почему бы не использовать async?\n- для чего path? почему бы не записать путь относительным?\n","plain_title":"после практики возникли следующие вопросы: - для чего тут асинхронный readFile? у меня с синхронным тесты прошли - если используюется асинхронный, то почему бы не использовать async? - для чего path? почему бы не записать путь относительным? ","creator":{"public_name":"Руслан Сухарев","id":170412,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":44445,"body":"1. Можно поменять да\n2. В курсах не давалось что есть promises в самом fs, так что этот вопрос оставлен на переделку\n3. Привычка. В cli приложения, пути часто берутся относительно рабочей директории.\n","topic_id":20956},{"creator":{"public_name":"Эдуард Кирютин","id":193581,"is_tutor":false},"id":47682,"body":"В данном кокретном случае да, но, имхо лучше сразу привыкать писать правильно","topic_id":20956},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":47684,"body":"Оборачивать нужно так https://nodejs.org/api/util.html#util_util_promisify_original","topic_id":20956},{"creator":{"public_name":"Руслан Сухарев","id":170412,"is_tutor":false},"id":47674,"body":"ну здесь же не в вебсервере, а до его старта читается файл\n","topic_id":20956},{"creator":{"public_name":"Эдуард Кирютин","id":193581,"is_tutor":false},"id":47689,"body":"Так вообще красота [v.2](https://ru.hexlet.io/code_reviews/80225?submission_id=106599)","topic_id":20956},{"creator":{"public_name":"Эдуард Кирютин","id":193581,"is_tutor":false},"id":47659,"body":"1. в веб-сервере при обращении к внешним ресурсам лучше не использовать синхронные функции, т.к. они на время выполнения блокируют остальные запросы. \n2. Функцию с колбеком легко переделать в promise - [вариант с async](https://ru.hexlet.io/code_reviews/80225)\n","topic_id":20956}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":49979,"title":"\n\n> Интерфейсы\n> \n> По правде говоря, этот адрес связан даже не с самим устройством, а с конкретным интерфейсом устройства. Например, каждая сетевая карта будет представлена в системе как отдельный интерфейс. Кроме того, интерфейсы бывают виртуальными, то есть у них отсутствует физический элемент. Зачем это нужно? Самый тривиальный пример это так называемая обратная петля (loopback). Интерфейс, который присутствует по умолчанию в большинстве ОС. Любой трафик, посланный в этот интерфейс, тут же принимается им же.\n> \n> Этот интерфейс позволяет обращаться к серверному приложению, расположенному на той же машине, без активного подключения к сети. Такая возможность особенно полезна для тестирования служб и их разработки. Адрес этого интерфейса всегда 127.0.0.1. Так же к нему можно обращаться по имени localhost.\n\n1. Например, каждая сетевая карта будет представлена в системе как отдельный интерфейс.\n\nКакой интерфейс может предоставить сетевая карта?\n\n2. Любой трафик, посланный в этот интерфейс, тут же принимается им же.\n\nОчень странно звучит. А как может быть по другому?\n","plain_title":"Интерфейсы По правде говоря, этот адрес связан даже не с самим устройством, а с конкретным интерфейсом устройства. Например, каждая сетевая карта будет представлена в системе как отдельный интерфейс. Кроме того, интерфейсы бывают виртуальными, то есть у них отсутствует физический элемент. Зачем это нужно? Самый тривиальный пример это так называемая обратная петля (loopback). Интерфейс, который присутствует по умолчанию в большинстве ОС. Любой трафик, посланный в этот интерфейс, тут же принимается им же. Этот интерфейс позволяет обращаться к серверному приложению, расположенному на той же машине, без активного подключения к сети. Такая возможность особенно полезна для тестирования служб и их разработки. Адрес этого интерфейса всегда 127.0.0.1. Так же к нему можно обращаться по имени localhost. Например, каждая сетевая карта будет представлена в системе как отдельный интерфейс. Какой интерфейс может предоставить сетевая карта? Любой трафик, посланный в этот интерфейс, тут же принимается им же. Очень странно звучит. А как может быть по другому? ","creator":{"public_name":"Daniyar Zhanakhmetov","id":239134,"is_tutor":false},"comments":[{"creator":{"public_name":"Sergei Melodyn","id":162475,"is_tutor":true},"id":107141,"body":"**Daniyar Zhanakhmetov**, приветствую.\n\n> Какой интерфейс может предоставить сетевая карта?\n\nЗдесь интерфейсы - не окна с кнопками, а предоставление доступа \"пользователям\" - операционной системе, запросам по сети. \n\n> А как может быть по другому\n\nМожет быть прокси, коммутатор или что-то ещё.","topic_id":49979}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":19027,"title":"Тесты проходят, но команда **make start** падает с ошибкой:\n```\nbin/phonebook\n/usr/src/app/bin/phonebook:3\nimport solution from '../server';\n ^^^^^^^^\n\nSyntaxError: Unexpected identifier\n at new Script (vm.js:73:7)\n at createScript (vm.js:245:10)\n at Object.runInThisContext (vm.js:297:10)\n at Module._compile (internal/modules/cjs/loader.js:657:28)\n at Module._extensions..js (internal/modules/cjs/loader.js:700:10)\n at Object.newLoader [as .js] (/usr/lib/node_modules/@babel/node/node_modules/pirates/lib/index.js:88:7)\n at Module.load (internal/modules/cjs/loader.js:599:32)\n at tryModuleLoad (internal/modules/cjs/loader.js:538:12)\n at Function.Module._load (internal/modules/cjs/loader.js:530:3)\n at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)\nMakefile:2: recipe for target 'start' failed\nmake: *** [start] Error 1\n```","plain_title":"Тесты проходят, но команда make start падает с ошибкой: ``` bin/phonebook /usr/src/app/bin/phonebook:3 import solution from '../server'; ^^^^^^^^ SyntaxError: Unexpected identifier at new Script (vm.js:73:7) at createScript (vm.js:245:10) at Object.runInThisContext (vm.js:297:10) at Module.compile (internal/modules/cjs/loader.js:657:28) at Module.extensions..js (internal/modules/cjs/loader.js:700:10) at Object.newLoader as .js (/usr/lib/node_modules/@babel/node/node_modules/pirates/lib/index.js:88:7) at Module.load (internal/modules/cjs/loader.js:599:32) at tryModuleLoad (internal/modules/cjs/loader.js:538:12) at Function.Module._load (internal/modules/cjs/loader.js:530:3) at Function.Module.runMain (internal/modules/cjs/loader.js:742:12) Makefile:2: recipe for target 'start' failed make: *** [start] Error 1 ``` ","creator":{"public_name":"","id":166516,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":40261,"body":"Ага, поправил","topic_id":19027}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":6496,"title":"в статье-\n\n$ telnet localhost 4000\nTrying ::1...\nConnected to localhost.\nEscape character is '^]'.\nGET / HTTP/1.1\nhost: hexlet.io\nconnection: close\n\nвроде запрос был к localhost а не к hexlet.io?","plain_title":"в статье- $ telnet localhost 4000 Trying ::1... Connected to localhost. Escape character is '^]'. GET / HTTP/1.1 host: hexlet.io connection: close вроде запрос был к localhost а не к hexlet.io? ","creator":{"public_name":"Дмитрий Самохвалов","id":122173,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":11959,"body":"Не понял вопроса. Где был? Какой должен быть?","topic_id":6496},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":11963,"body":"Место где запущен сервер и заголовок `host` в протоколе `http` друг с другом никак не связаны. На одном сервере может быть запущено 100 разных сайтов. Соответственно именно заголовок `host: hexlet.io` определяет то, какой сайт вам отдаст веб-сервер.","topic_id":6496},{"creator":{"public_name":"Дмитрий Самохвалов","id":122173,"is_tutor":false},"id":11961,"body":"в статье, в примере с telnet -ом, почему host: hexlet.io ? сервер был запущен на localhost ?\nили я чего-то не понял..","topic_id":6496}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":49949,"title":"https://ru.hexlet.io/code_reviews/346632\n\nJest ругается, тесты проходят.\n```\nJest has detected the following 1 open handle potentially keeping Jest from exiting:\n\n ● TCPSERVERWRAP\n\n 14 | response.end(messages.join('\\n'));\n 15 | });\n > 16 | server.listen(port, callback);\n | ^\n 17 | // END\n 18 | };\n 19 | \n```\n\n\nЕще не совсем понял, как работать с этим в консоли.\n\n`make start` затем `curl localhost:8080` ничего не дает.\n","plain_title":"https://ru.hexlet.io/code_reviews/346632 Jest ругается, тесты проходят. ``` Jest has detected the following 1 open handle potentially keeping Jest from exiting: ● TCPSERVERWRAP 14 | response.end(messages.join('\\n')); 15 | }); > 16 | server.listen(port, callback); | ^ 17 | // END 18 | }; 19 | ","creator":{"public_name":"Daniyar Zhanakhmetov","id":239134,"is_tutor":false},"comments":[{"creator":{"public_name":"Avshukan","id":342842,"is_tutor":false},"id":123046,"body":"**Daniyar Zhanakhmetov**, \nТакое же предупреждение получил.\nТак и не понял, строка server.listen(...\nтак и должна быть или что-то надо поправить","topic_id":49949},{"creator":{"public_name":"Daniyar Zhanakhmetov","id":239134,"is_tutor":false},"id":123219,"body":"**Avshukan**, Вам необходимо задать вопрос в отдельной ветке, иначе менторы не увидят ваш вопрос","topic_id":49949},{"creator":{"public_name":"Avshukan","id":342842,"is_tutor":false},"id":123270,"body":"**Daniyar Zhanakhmetov**, Понял! Спасибо!","topic_id":49949},{"creator":{"public_name":"Stanislav Dzisiak","id":212236,"is_tutor":true},"id":107084,"body":"Приветствую, Данияр!\n\nJest выдаёт предупреждение, о том что потенциально тест может быть не завершен, но сам тест отрабатывает корректно. Такое поведение происходит из-за передачи асинхронной функции в createServer(). Порекомендую вам посмотреть в решении учителя как реализован этот момент.\n\n> Еще не совсем понял, как работать с этим в консоли.\n\nЯ проверил, всё работает корректно. Вам нужно в одном окне терминала запустить `make start`, а затем в другом окне с терминалом выполнить запрос с помощью `curl`. Вы должны увидеть ответ сервера:\n\n /usr/src/app$ curl localhost:8080\n Welcome to The Phonebook\n Records count: 1000","topic_id":49949}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":6182,"title":"Что нужно сделать, чтобы заработал \"Web-доступ\". Что то нужно в терминале запустить?","plain_title":"Что нужно сделать, чтобы заработал \"Web-доступ\". Что то нужно в терминале запустить? ","creator":{"public_name":"Andrew Titov","id":9330,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":11249,"body":"Цитата из текста:\n\n```\nКроме запуска тестов, обязательно попробуйте \"поиграть\" с написанным сервером. Для этого воспользуйтесь командой make start, которая запускает ваш сервер.\n```","topic_id":6182},{"creator":{"public_name":"Andrew Titov","id":9330,"is_tutor":false},"id":11251,"body":"Это даже в вопросах у кого то было. Искать не очень удобно получается и по урокам и по ответам. Приходится просматривать всё подряд по очереди.","topic_id":6182}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}},{"id":28199,"title":"Не понял, что нужно тесту. Он повисает с таймаутом\n```\n Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.\n```\nПри том, в cli всё работает. И локально в ноде работает. Пробовал оборачивать и в функцию сервера и наоборот в readFile. Тест брать не хочет, в чём прикол? ¯\\_(ツ)_/¯\n\n\n\n\n","plain_title":"Не понял, что нужно тесту. Он повисает с таймаутом Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout. При том, в cli всё работает. И локально в ноде работает. Пробовал оборачивать и в функцию сервера и наоборот в readFile. Тест брать не хочет, в чём прикол? ¯_(ツ)_/¯ Терминал https://i.ibb.co/DV3f2m0/Screenshot-from-2019-05-22-16-53-23.png ","creator":{"public_name":"Сергей Шевцов","id":162787,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":60655,"body":"Вполне вероятно что вы не передаете колбек в listen из-за этого тесты не могут остановить сервер.","topic_id":28199},{"creator":{"public_name":"Сергей Шевцов","id":162787,"is_tutor":false},"id":60668,"body":"В точку, Кирилл. Я написал свой, вместо того чтобы пробросить тот, который в аргументе передавался :)","topic_id":28199}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Реализация сервера","entity_url":null,"active":true}}],"lesson":{"exercise":null,"units":[{"id":1558,"name":"theory","url":"/courses/js-http-server/lessons/example/theory_unit"},{"id":2464,"name":"quiz","url":"/courses/js-http-server/lessons/example/quiz_unit"}],"links":[{"id":425564,"name":"Что такое DNS","url":"https://guides.hexlet.io/ru/dns/"}],"ordered_units":[{"id":1558,"name":"theory","url":"/courses/js-http-server/lessons/example/theory_unit"},{"id":2464,"name":"quiz","url":"/courses/js-http-server/lessons/example/quiz_unit"}],"id":763,"slug":"example","state":"approved","name":"Реализация сервера","course_order":20,"goal":"Изучаем основы, которые помогут понять принципы работы сетевых демонов","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Перед тем, как бросаться в омут и смотреть на реализацию бэкэнда, нам придётся поговорить\nо теоретической базе, скрывающейся за несколькими строчками кода. Небольшого введения\nдолжно хватить для старта, но в будущем нужно будет браться за соответствующие книги\nи углубляться в тему. Конечно же, это верно для тех, кто хочет стать хорошим разработчиком.\nПугаться этих книжек не стоит, даже наоборот, после них вы можете почувствовать кураж,\nпотому что огромное число вещей, которые для вас сейчас представляются черным ящиком,\nна поверку окажутся достаточно простыми концепциями с понятным устройством внутри.\n\n## Операционная система (ОС)\n\nОсновой основ для программиста можно считать ОС. Да, у нас есть еще железо и архитектуру\nЭВМ никто не отменял, но прикладному разработчику достаточно иметь общее понимание того,\nкак работает железо, а вот от ОС не уйти совсем.\n\n### Процесс\n\nБазовой единицей исполнения в ОС является процесс. Каждый раз, когда вы запускаете\nкакую-либо программу, то запускается минимум один процесс. Кстати, может быть и больше,\nотсюда следует что программа != процесс.\nСписок запущенных процессов можно посмотреть так:\n\n```bash\nps -xf\n\nPID TTY STAT TIME COMMAND\n29 pts/1 S 0:00 bash\n30 pts/1 S+ 0:00 \\_ top\n22 pts/0 S 0:00 bash\n32 pts/0 R+ 0:00 \\_ ps xf\n```\n\nКаждая строчка на рисунке выше – это информация о каком-либо процессе. Как видно,\nОС содержит в себе различную информацию о каждом из процессов. На текущий момент\nнас интересует только один параметр, который называется `PID`. PID, как ни странно,\nрасшифровывается как `process identifier` и фактически представляет из себя целое\nчисло, однозначно определяющее то, о каком процессе идет речь.\n\n## Сети\n\nОсновным (высокоуровневым) способом коммуникации между машинами является семейство\nпротоколов `TCP/IP`. Большинство людей, которые пользуются интернетом или\nсетями в целом так или иначе слышали выражение `ip адрес`. Этот адрес (для версии `IPv4`)\nможет выглядеть так: `10.0.152.23`. Он указывает на какое-то устройство в сети,\nкоторое не обязательно представлено компьютером в привычном понимании этого слова.\n\n### Интерфейсы\n\nПо правде говоря, этот адрес связан даже не с самим устройством, а с конкретным\nинтерфейсом устройства. Например, каждая сетевая карта будет представлена в системе\nкак отдельный интерфейс. Кроме того, интерфейсы бывают виртуальными, то есть\nу них отсутствует физический элемент. Зачем это нужно? Самый тривиальный пример\nэто так называемая обратная петля (loopback). Интерфейс, который присутствует\nпо умолчанию в большинстве ОС. Любой трафик, посланный в этот интерфейс, тут же\nпринимается им же.\n\nЭтот интерфейс позволяет обращаться к серверному приложению, расположенному на той\nже машине, без активного подключения к сети. Такая возможность особенно полезна\nдля тестирования служб и их разработки. Адрес этого интерфейса всегда `127.0.0.1`.\nТак же к нему можно обращаться по имени `localhost`.\n\n## Domain Name System\n\n`DNS` - система доменных имён, благодаря которой нам необходимо запоминать только\nбуквенные имена сайтов без необходимости знать конкретный `ip` адрес машины,\nна которую надо пойти. Общий принцип работы довольно прост. Каждый раз,\nкогда в браузер вводится имя сайта, он обращается к специальным серверам\nи спрашивает их: 'Какой ip адрес у hexlet.io?'. Дальше происходит немного магии,\nи, в конце концов, эта система возвращает (если найдёт) этот адрес. Затем\nбраузер устанавливает `tcp` соединение и начинает свою работу.\n\n## Порты\n\nКогда происходит общение удаленных машин друг с другом по `tcp`, то в реальности\nмежду собой общаются процессы ОС, а не компьютеры в целом. Отсюда возникает вопрос:\nкаким образом, зная только `ip` адрес, постучаться на чужую машину в интересующую\nнас программу. Конкретно в этом курсе нас интересует веб-сервер.\nКороткий ответ: никак. И действительно, одного `ip` адреса недостаточно.\n\nВ `tcp` существует такое понятие, как порт. Это целое число, означающее точку входа\nв процесс запущенной программы. То есть при получении данных по `tcp` ОС смотрит то,\nдля какого порта они предназначены. Затем она находит процесс, соответствующий этому\nпорту, и передает данные в него. Важным следствием этого подхода становится тот\nфакт, что невозможно занять уже занятый порт. Иначе это ввело бы неоднозначность.\nИ действительно, при старте сервера, который пытается слушать занятый другой программой порт,\nбудет получена такая ошибка:\n\n```bash\nError: listen EADDRINUSE :::4000\n```\n\nМы можем даже посмотреть на того, кто занял этот порт:\n\n```bash\nlsof -i :4000\n\nCOMMAND PID USER TYPE NODE NAME\nnode 40726 mokevnin IPv6 TCP *:terabase (LISTEN)\n```\n\nСуммируя вышесказанное, делаем вывод, что любое серверное приложение при старте\nдолжно начать слушать определенный порт для возможности получать данные по сети.\n\nЕсли вернуться к браузеру, то может возникнуть вопрос: почему мы не указываем порт,\nкогда загружаем сайты, откуда браузер знает, куда стучаться на сервер? Ответ\nна этот вопрос крайне прост: браузер действительно знает порт, на который\nнужно идти. И по умолчанию это порт с номером `80`.\n\n## Веб-сервер\n\nНачнём с иллюстрации:\n\n```javascript\n// server.js\nimport http from 'http'\n\nconst server = http.createServer((request, response) => {\n // content-length формируется автоматически!\n response.write('hello, world!')\n response.end()\n})\n\nconst port = 4000\nserver.listen(port, () => {\n console.log('Server has been started')\n})\n```\n\n1. Импортируется модуль `http`. Он встроен в `node.js` и позволяет создать\n веб-сервер (и не только).\n1. Создаётся веб-сервер. В функцию `createServer` передается обработчик запросов. Он будет вызываться на каждый входящий запрос.\n1. Сервер вешается на порт.\n\nОбработки запроса в данном примере как таковой нет. Сервер будет отвечать\nпо http фразой `hello, world!` на любой входящий запрос. Делается это\nс помощью объекта `response`, который представляет собой http-ответ.\nВ примере выше мы используем две функции интерфейса `response`. Функцию `write`, которая позволяет передать текст в теле `http` ответа, и функцию `end`,\nкоторая означает, что мы закончили формирование ответа. Обратите внимание:\nнам не пришлось руками выставлять заголовок `content-length`, модуль `http`\nсамостоятельно вычисляет размер тела и подставляет необходимый заголовок.\n\nДля запуска нашего сервера необходимо набрать команду:\n\n```bash\nnode server.js # blocking\n```\n\nЗапуск приводит к блокировке. Сервер запущен и работает. Чтобы его остановить,\nнадо нажать комбинацию `ctrl+c`.\n\nТеперь можно выполнить запрос к серверу:\n\n[](https://asciinema.org/a/4AXmKVk7GCsYS7QqXfSyN4kCk)\n\nПроделав данную процедуру, можно будет сказать, что вы запустили свой первый сайт\nна node.js ;)\n\nНо дальше нас поджидает сюрприз. Если попробовать поменять код сервера, и писать\nв ответ `my first web server`, то без перезапуска сервера ничего не изменится.\nПосле того, как сервер запущен, он больше не перечитывает файлы с диска и не\nизменяет своего состояния. Такое поведение не является особенностью модуля `http`,\nтак устроен сам язык. Поэтому не забывайте перезапускать сервер, после внесения\nизменений.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":1557,"name":"theory","url":"/courses/js-http-server/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":1557,"name":"theory","url":"/courses/js-http-server/lessons/intro/theory_unit"}],"id":768,"slug":"intro","state":"approved","name":"Введение","course_order":10,"goal":"Знакомимся с курсом и проектом","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Под веб-разработкой обычно понимают создание клиент-серверных приложений или, как мы обычно говорим, сайтов. Типичный сайт представляет из себя бэкэнд-приложение, которое крутится на сервере, и клиент. Под клиентом чаще всего подразумевается браузер. Он также является тонким клиентом, то есть браузер, сам по себе, не содержит логики, а отправляет запросы на сервер и занимается только отрисовкой данных, пришедших с сервера. С другой стороны, современные сайты содержат богатую логику на клиенте, что, по сути, превращает работающий сайт в полноценное приложение, которое отрабатывает на компьютере\nпользователя. Но это уже совсем другая история ;)\n\nОсновным способом коммуникации между браузером и бэкэндом сайта является протокол http. Он относится к классу текстовых протоколов, которые можно легко читать и понимать, что происходит. Это довольно удобно, к тому же http работает достаточно просто, и хорошее понимание его работы делает из вас гораздо более ценного специалиста.\n\n\n\nВ этом курсе мы научимся делать бэкэнд сайта, который представляет из себя сервис для ведения телефонной книги.\n\n```bash\nbin/phonebook\n\nhexlet-phonebook server was started on localhost:4000 +0ms\n\ncurl localhost:4000\n\nWelcome to The Phonebook\nRecords count: 1000\n```\n\nВ процессе разработки изучим множество тем и подходов, связанных с написанием подобных приложений. Основные темы:\n\n* Базовые сведения об операционных системах\n* Основы сетей и TCP/IP\n* Понятие HTTP сессии. Понимание модели запрос/ответ\n* Форматы данных, JSON\n* Работа с инструментами для выполнения запросов telnet/curl\n* Обработка ошибок. Логгирование\n\nКроме перечисленного выше, познакомимся со множеством модулей Node.js и полезными пакетами для более удобной разработки.\n\n* http, fs, querystring, url\n* debug, nodemon\n"},"id":131,"slug":"js-http-server","challenges_count":0,"name":"JS: HTTP Server","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"free","description":"На этом курсе вы изучите HTTP-Server. Вы узнаете больше о формате json, роутинге и стандартных модулях Node.js, которые упрощают веб-разработку. В итоге вы научитесь создавать архитектуру приложения в соответствии с MVC. HTTP-Server пригодится, если вы решите создавать несложные сайты. Знания из этого курса помогают программистам лучше понять концепцию запрос-ответ.","kind":"sandbox","updated_at":"2026-01-20T11:54:03.682Z","language":"javascript","duration_cache":8700,"skills":["Создавать сайты с использованием встроенного в Node.js модуля HTTP","Строить архитектуру приложения в соответствии с MVC","Описывать динамические маршруты и обработчики для них"],"keywords":["роутинг","curl","json"],"lessons_count":8,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6OTA2MCwicHVyIjoiYmxvYl9pZCJ9fQ==--a59331b70f1230fc85bda2f741077b822e89c5bd/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJqcGciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--39ba06fa99226096df9fc6bb31f84e1d29ea98e9/image.png"},"recommendedLandings":[],"lessonMemberUnit":null,"accessToLearnUnitExists":true,"accessToCourseExists":true},"url":"/courses/js-http-server/lessons/example/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">JS: HTTP Server</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Реализация сервера</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Реализация сервера","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"JS: HTTP Server"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>Перед тем, как бросаться в омут и смотреть на реализацию бэкэнда, нам придётся поговорить
о теоретической базе, скрывающейся за несколькими строчками кода. Небольшого введения
должно хватить для старта, но в будущем нужно будет браться за соответствующие книги
и углубляться в тему. Конечно же, это верно для тех, кто хочет стать хорошим разработчиком.
Пугаться этих книжек не стоит, даже наоборот, после них вы можете почувствовать кураж,
потому что огромное число вещей, которые для вас сейчас представляются черным ящиком,
на поверку окажутся достаточно простыми концепциями с понятным устройством внутри.</p>
<h2 id="heading-2-1">Операционная система (ОС)</h2>
<p>Основой основ для программиста можно считать ОС. Да, у нас есть еще железо и архитектуру
ЭВМ никто не отменял, но прикладному разработчику достаточно иметь общее понимание того,
как работает железо, а вот от ОС не уйти совсем.</p>
<h3 id="heading-3-2">Процесс</h3>
<p>Базовой единицей исполнения в ОС является процесс. Каждый раз, когда вы запускаете
какую-либо программу, то запускается минимум один процесс. Кстати, может быть и больше,
отсюда следует что программа != процесс.
Список запущенных процессов можно посмотреть так:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">ps -xf
PID TTY STAT TIME COMMAND
29 pts/1 S 0:00 bash
30 pts/1 S+ 0:00 \_ top
22 pts/0 S 0:00 bash
32 pts/0 R+ 0:00 \_ ps xf</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Каждая строчка на рисунке выше – это информация о каком-либо процессе. Как видно,
ОС содержит в себе различную информацию о каждом из процессов. На текущий момент
нас интересует только один параметр, который называется <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">PID</code>. PID, как ни странно,
расшифровывается как <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">process identifier</code> и фактически представляет из себя целое
число, однозначно определяющее то, о каком процессе идет речь.</p>
<h2 id="heading-2-3">Сети</h2>
<p>Основным (высокоуровневым) способом коммуникации между машинами является семейство
протоколов <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">TCP/IP</code>. Большинство людей, которые пользуются интернетом или
сетями в целом так или иначе слышали выражение <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ip адрес</code>. Этот адрес (для версии <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">IPv4</code>)
может выглядеть так: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">10.0.152.23</code>. Он указывает на какое-то устройство в сети,
которое не обязательно представлено компьютером в привычном понимании этого слова.</p>
<h3 id="heading-3-4">Интерфейсы</h3>
<p>По правде говоря, этот адрес связан даже не с самим устройством, а с конкретным
интерфейсом устройства. Например, каждая сетевая карта будет представлена в системе
как отдельный интерфейс. Кроме того, интерфейсы бывают виртуальными, то есть
у них отсутствует физический элемент. Зачем это нужно? Самый тривиальный пример
это так называемая обратная петля (loopback). Интерфейс, который присутствует
по умолчанию в большинстве ОС. Любой трафик, посланный в этот интерфейс, тут же
принимается им же.</p>
<p>Этот интерфейс позволяет обращаться к серверному приложению, расположенному на той
же машине, без активного подключения к сети. Такая возможность особенно полезна
для тестирования служб и их разработки. Адрес этого интерфейса всегда <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">127.0.0.1</code>.
Так же к нему можно обращаться по имени <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">localhost</code>.</p>
<h2 id="heading-2-5">Domain Name System</h2>
<p><code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">DNS</code> - система доменных имён, благодаря которой нам необходимо запоминать только
буквенные имена сайтов без необходимости знать конкретный <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ip</code> адрес машины,
на которую надо пойти. Общий принцип работы довольно прост. Каждый раз,
когда в браузер вводится имя сайта, он обращается к специальным серверам
и спрашивает их: 'Какой ip адрес у hexlet.io?'. Дальше происходит немного магии,
и, в конце концов, эта система возвращает (если найдёт) этот адрес. Затем
браузер устанавливает <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">tcp</code> соединение и начинает свою работу.</p>
<h2 id="heading-2-6">Порты</h2>
<p>Когда происходит общение удаленных машин друг с другом по <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">tcp</code>, то в реальности
между собой общаются процессы ОС, а не компьютеры в целом. Отсюда возникает вопрос:
каким образом, зная только <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ip</code> адрес, постучаться на чужую машину в интересующую
нас программу. Конкретно в этом курсе нас интересует веб-сервер.
Короткий ответ: никак. И действительно, одного <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ip</code> адреса недостаточно.</p>
<p>В <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">tcp</code> существует такое понятие, как порт. Это целое число, означающее точку входа
в процесс запущенной программы. То есть при получении данных по <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">tcp</code> ОС смотрит то,
для какого порта они предназначены. Затем она находит процесс, соответствующий этому
порту, и передает данные в него. Важным следствием этого подхода становится тот
факт, что невозможно занять уже занятый порт. Иначе это ввело бы неоднозначность.
И действительно, при старте сервера, который пытается слушать занятый другой программой порт,
будет получена такая ошибка:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">Error: listen EADDRINUSE :::4000</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Мы можем даже посмотреть на того, кто занял этот порт:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">lsof -i :4000
COMMAND PID USER TYPE NODE NAME
node 40726 mokevnin IPv6 TCP *:terabase (LISTEN)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Суммируя вышесказанное, делаем вывод, что любое серверное приложение при старте
должно начать слушать определенный порт для возможности получать данные по сети.</p>
<p>Если вернуться к браузеру, то может возникнуть вопрос: почему мы не указываем порт,
когда загружаем сайты, откуда браузер знает, куда стучаться на сервер? Ответ
на этот вопрос крайне прост: браузер действительно знает порт, на который
нужно идти. И по умолчанию это порт с номером <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">80</code>.</p>
<h2 id="heading-2-7">Веб-сервер</h2>
<p>Начнём с иллюстрации:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">// server.js
import http from 'http'
const server = http.createServer((request, response) => {
// content-length формируется автоматически!
response.write('hello, world!')
response.end()
})
const port = 4000
server.listen(port, () => {
console.log('Server has been started')
})</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<ol>
<li>Импортируется модуль <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">http</code>. Он встроен в <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">node.js</code> и позволяет создать
веб-сервер (и не только).</li>
<li>Создаётся веб-сервер. В функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">createServer</code> передается обработчик запросов. Он будет вызываться на каждый входящий запрос.</li>
<li>Сервер вешается на порт.</li>
</ol>
<p>Обработки запроса в данном примере как таковой нет. Сервер будет отвечать
по http фразой <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">hello, world!</code> на любой входящий запрос. Делается это
с помощью объекта <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">response</code>, который представляет собой http-ответ.
В примере выше мы используем две функции интерфейса <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">response</code>. Функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">write</code>, которая позволяет передать текст в теле <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">http</code> ответа, и функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">end</code>,
которая означает, что мы закончили формирование ответа. Обратите внимание:
нам не пришлось руками выставлять заголовок <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">content-length</code>, модуль <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">http</code>
самостоятельно вычисляет размер тела и подставляет необходимый заголовок.</p>
<p>Для запуска нашего сервера необходимо набрать команду:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">node server.js # blocking</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Запуск приводит к блокировке. Сервер запущен и работает. Чтобы его остановить,
надо нажать комбинацию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ctrl+c</code>.</p>
<p>Теперь можно выполнить запрос к серверу:</p>
<p><a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://asciinema.org/a/4AXmKVk7GCsYS7QqXfSyN4kCk" rel="noopener noreferrer" target="_blank"><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6OTA2MiwicHVyIjoiYmxvYl9pZCJ9fQ==--76973c47ee4533d6cadb58b09f8739eeae59a6d7/4AXmKVk7GCsYS7QqXfSyN4kCk.gif" alt="asciicast" loading="lazy"/></a></p>
<p>Проделав данную процедуру, можно будет сказать, что вы запустили свой первый сайт
на node.js ;)</p>
<p>Но дальше нас поджидает сюрприз. Если попробовать поменять код сервера, и писать
в ответ <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">my first web server</code>, то без перезапуска сервера ничего не изменится.
После того, как сервер запущен, он больше не перечитывает файлы с диска и не
изменяет своего состояния. Такое поведение не является особенностью модуля <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">http</code>,
так устроен сам язык. Поэтому не забывайте перезапускать сервер, после внесения
изменений.</p></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/js-http-server/lessons/example/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 8</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/js-http-server/lessons/example/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>