Создаем надёжный обработчик HTTP-запросов (локальный HTTP-сервер) на компьютере. Наша цель — запустить у себя локальную веб-службу, которая умеет одновременно отвечать многим посетителям, корректно завершаться по Ctrl+C и не путаться в общих данных между потоками. По дороге объясним, что и где нажимать, чтобы не теряться.
Введение
Сегодня почти каждое приложение так или иначе работает с Интернетом. Даже самые простые проекты требуют обмена данными: браузер отправляет запрос, программа отвечает. Обычно мы не задумываемся, как это устроено внутри, ведь за нас всё делает веб-сервер или хостинг-платформа. Но если вы разрабатываете сервис, тестируете API или просто хотите глубже понять механику взаимодействия клиента и сервера, полезно уметь поднять собственный обработчик HTTP-запросов у себя на компьютере.
Такой навык решает сразу несколько задач. Во-первых, вы можете локально проверять работу приложения без аренды сервера и без риска «сломать» боевой сайт. Во-вторых, это удобный способ изучить основы сетевого программирования на Python: от запуска простого HTTP-сервера до многопоточности и корректного завершения работы. В-третьих, вы получаете гибкость — можно написать свой минималистичный сервер, который будет вести логи, возвращать тестовые данные, эмулировать ответы API или проверять нагрузку.
В этой статье мы пошагово пройдём путь от однострочного «Hello, world!» до многопоточной веб-службы, которая корректно завершается по Ctrl+C и умеет принимать POST-запросы. Всё это — средствами стандартной библиотеки Python, без сторонних пакетов и сложной настройки.
Перед стартом
Если Python уже установлен — отлично. Если нет, берём его на официальном сайте python.org в разделе «Downloads» и ставим обычным способом для своей системы. Там же есть подсказка, какую версию выбрать.
Где работаем и куда всё складываем
Откройте окно с командной строкой:
- macOS или Linux — «Терминал»;
- Windows — «PowerShell».
Создадим удобное пространство на рабочем столе и перейдём в него.
Для macOS или Linux:
mkdir -p ~/Desktop/http_lab
cd ~/Desktop/http_lab
Для Windows PowerShell:
mkdir "$env:USERPROFILE\Desktop\http_lab"
cd "$env:USERPROFILE\Desktop\http_lab"
Здесь и дальше мы будем создавать текстовые документы с расширением .py и запускать их именно из той директории, где они находятся.
Порт — номер «дверцы» в вашем компьютере, через которую программы общаются по сети. Например, будем слушать порт 8000.
HTTP — простой протокол общения браузера и вашей программы: браузер спрашивает, программа отвечает.
Многопоточность — способ делать несколько дел одновременно в одном процессе: для каждого посетителя создаётся отдельная «нитка» выполнения.
Шаг 1. Проверяем, какой у нас Python, и где он находится
В окне командной строки:
python3 --version
В Windows может сработать:
python --version
Если видите что-то вроде Python 3.10… — можно идти дальше.
Шаг 2. Самый компактный обработчик HTTP-запросов
Создаём документ с кодом:
- macOS или Linux — откройте терминал и наберите: nano hello_http.py;
- Windows (с Python 3 установленным) — можно использовать любой текстовый редактор, например Notepad, и сохранить файл как hello_http.py в папку http_lab.
Вставляем в открывшийся редактор:
from http.server import BaseHTTPRequestHandler, HTTPServer
class SimpleHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200) # код ответа «ОК»
self.send_header("Content-type", "text/plain") # тип данных — текст
self.end_headers()
self.wfile.write(b"Hello, world!") # выводим сообщение
# создаём сервер на локальном интерфейсе (127.0.0.1) и порту 8000
server = HTTPServer(('127.0.0.1', 8000), SimpleHandler)
print("Сервер запущен на порту 8000")
server.serve_forever()
Сохраняем и выходим:
- в nano: Ctrl+O → Enter, затем Ctrl+X;
- в Windows сохраняем как обычный текстовый документ и закрываем редактор.
Запускаем сервер в терминале, находясь в папке http_lab, командой:
python3 hello_http.py
На экране появится сообщение:
Сервер запущен на порту 8000
Для проверки работы откройте браузер и перейдите по адресу: http://localhost:8000.
Увидите сообщение «Hello, world!».
Этот код создаёт самый простой HTTP-обработчик, который отвечает на каждый GET-запрос текстом.
HTTPServer — это основной компонент, который слушает порт и принимает запросы.
BaseHTTPRequestHandler — класс, который описывает, как сервер реагирует на запросы.
Порт 8000 — «номер входа» для связи браузера с сервером.
Шаг 3. Делаем параллельную обработку запросов
Теперь превратим однозадачную программу в многопоточную. В Python есть специальный класс ThreadingHTTPServer. Он обрабатывает каждый входящий запрос в отдельной «нити». Это стандартная библиотека, ничего дополнительно ставить не нужно. Класс появился в Python 3.7 и повторяет поведение обычного HTTPServer, но с потоками. Дополнительно используется замок (lock), чтобы разные потоки не мешали друг другу при работе с общими данными.
Все действия выполняем в папке http_lab.
Создаём файл. В терминале набираем:
nano threaded_http.py
Откроется текстовый редактор nano. В него копируем весь следующий код:
# threaded_http.py
import os
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
# Общий счётчик обращений
# LOCK — примитив синхронизации, чтобы потоки не "толкались"
REQUESTS = 0
LOCK = threading.Lock()
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
global REQUESTS
with LOCK:
REQUESTS += 1
number = REQUESTS
message = (
f"Здравствуй, гость номер {number}!\n"
f"Путь: {self.path}\n"
f"Поток: {threading.get_ident()}\n"
f"Процесс: {os.getpid()}\n"
)
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(message.encode("utf-8"))
class SafeThreadingHTTPServer(ThreadingHTTPServer):
daemon_threads = True # не ждём завершения дочерних потоков при выходе
allow_reuse_address = True # можно быстро перезапускать на том же порту
if __name__ == "__main__":
host, port = "127.0.0.1", 8000
httpd = SafeThreadingHTTPServer((host, port), Handler)
print(f"Работаю на http://{host}:{port} — жму руку многим гостям одновременно.")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nПоймал Ctrl+C, ухожу корректно…")
httpd.shutdown()
httpd.server_close()
print("Готово. До встречи!")
Сохраняем и выходим:
- Ctrl+O → Enter;
- Ctrl+X.
Проверяем, что файл есть:
ls
Появится threaded_http.py в списке.
Включение многопоточного сервера:
python3 threaded_http.py
В терминале увидите сообщение:
«Работаю на http://127.0.0.1:8000 — жму руку многим гостям одновременно.»
Сервер готов принимать несколько соединений одновременно.
Чтобы остановить работу, нажимаем Ctrl+C. Сессия корректно завершится, не оставляя «зависших» потоков.
Шаг 4. Что такое «корректное завершение»
И почему мы его делаем именно так?
serve_forever запускает внутренний цикл ожидания и обработки запросов. Метод shutdown() останавливает этот цикл безопасно, а затем server_close() закрывает сетевую «дверцу». Нажатие Ctrl+C вызывает KeyboardInterrupt, который мы перехватываем в блоке try…except и вызываем остановку.
Потоки и «замки»
Когда несколько ниток меняют общую переменную, легко получить путаницу. Именно поэтому мы оборачиваем обновление счётчика в with LOCK. Это делает блок кода «проходом по одному», исключая гонки.
Шаг 5. Добавим ответы на POST и «эхо» полезных данных
Создадим документ echo_http.py в том же каталоге http_lab:
# echo_http.py
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import threading
class EchoHandler(BaseHTTPRequestHandler):
def do_POST(self):
size = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(size) if size else b""
reply = f"Получил {size} байт. Тело:\n{body.decode('utf-8', errors='replace')}"
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(reply.encode("utf-8"))
def do_GET(self):
message = "Отправьте POST на этот же адрес — пришлю вам всё, что получил."
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(message.encode("utf-8"))
class S(ThreadingHTTPServer):
daemon_threads = True
allow_reuse_address = True
if __name__ == "__main__":
srv = S(("127.0.0.1", 8000), EchoHandler)
print("Жду письма методом POST на http://127.0.0.1:8000")
try:
srv.serve_forever()
except KeyboardInterrupt:
srv.shutdown()
srv.server_close()
Как проверить POST без лишних программ
Откройте новое окно терминала, останьтесь в любой папке и отправьте:
curl -X POST -d "Привет из терминала" http://127.0.0.1:8000
В ответ придёт то, что отправили.
У нас получилась рабочая веб-служба, которая:
- одновременно отвечает многим гостям;
- аккуратно закрывается по Ctrl+C;
- не конфликтует внутри при обращении к общим данным.
Заключение
Мы шаг за шагом собрали локальный обработчик HTTP-запросов на Python: от минимального примера до многопоточной службы, которая корректно завершает работу и умеет принимать POST-запросы. Такой опыт полезен не только начинающим разработчикам, но и системным администраторам: с помощью небольшого скрипта можно отладить API, протестировать нагрузку или быстро проверить сетевое взаимодействие.
В реальных условиях подобные умения особенно ценны при работе на сервере или VPS. Там, где нет готовых инструментов или нужна тонкая настройка, собственный мини-сервер позволяет быстро диагностировать проблему и контролировать, что именно получает и отправляет ваше приложение.
Дальше вы можете развивать этот пример: добавлять обработку разных путей, сохранять данные в файлы, вести логи или интегрировать с базой данных. Python даёт для этого все необходимые средства, а базовые знания о многопоточности и корректном завершении процесса помогут писать более надёжные сервисы.
Читайте в блоге:
- Базовое администрирование Linux через Python
- Как построить простой сервер на Python: от нуля до работающего результата
- Python для аутентификации: практическая схема клиент-серверного входа