Создаём надёжный обработчик HTTP-запросов

Создаём надёжный обработчик HTTP-запросов

Создаем надёжный обработчик HTTP-запросов (локальный HTTP-сервер) на компьютере. Наша цель — запустить у себя локальную веб-службу, которая умеет одновременно отвечать многим посетителям, корректно завершаться по Ctrl+C и не путаться в общих данных между потоками. По дороге объясним, что и где нажимать, чтобы не теряться.

Введение

Сегодня почти каждое приложение так или иначе работает с Интернетом. Даже самые простые проекты требуют обмена данными: браузер отправляет запрос, программа отвечает. Обычно мы не задумываемся, как это устроено внутри, ведь за нас всё делает веб-сервер или хостинг-платформа. Но если вы разрабатываете сервис, тестируете API или просто хотите глубже понять механику взаимодействия клиента и сервера, полезно уметь поднять собственный обработчик HTTP-запросов у себя на компьютере.

Такой навык решает сразу несколько задач. Во-первых, вы можете локально проверять работу приложения без аренды сервера и без риска «сломать» боевой сайт. Во-вторых, это удобный способ изучить основы сетевого программирования на Python: от запуска простого HTTP-сервера до многопоточности и корректного завершения работы. В-третьих, вы получаете гибкость — можно написать свой минималистичный сервер, который будет вести логи, возвращать тестовые данные, эмулировать ответы API или проверять нагрузку.

В этой статье мы пошагово пройдём путь от однострочного «Hello, world!» до многопоточной веб-службы, которая корректно завершается по Ctrl+C и умеет принимать POST-запросы. Всё это — средствами стандартной библиотеки Python, без сторонних пакетов и сложной настройки.

Аренда VPS/VDS — от ₽219/месяц

Почему выбирают VPS от AdminVPS:

✓ Дешевле физического сервера

✓ Более гибкий и мощный, чем обычный хостинг

✓ Бесплатная защита от DDoS и техподдержка 24/7

✓ Масштабируется под любые задачи

Виртуальный сервер VPS/VDS — ваш личный сервер для сайтов, магазинов, ботов и других проектов.

popup12

Перед стартом

Если 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 даёт для этого все необходимые средства, а базовые знания о многопоточности и корректном завершении процесса помогут писать более надёжные сервисы.

Читайте в блоге:

Loading spinner
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

Нужен VPS сервер?

Арендуйте мощный VPS сервер для ваших проектов! Быстрая настройка, высокая производительность и надежная поддержка 24/7. Начните прямо сейчас!

Что будем искать? Например,VPS-сервер

Мы в социальных сетях