Сервер разработки на Python: от статики до кастомных обработчиков
Основные способы запуска HTTP сервера на Python
Встроенный модуль http.server позволяет быстро раздать статические файлы по протоколу HTTP. Это удобно для локального тестирования веб-приложений, обмена файлами или отладки HTML-страниц. Ниже рассматриваются различные сценарии использования.
Как запустить базовый HTTP сервер одной командой?
Для запуска сервера, который обслуживает содержимое текущей директории, выполняется команда:
python3 -m http.server 8000Python start server (запуск сервера python)
После запуска сервер доступен по адресу http://localhost:8000. Параметр 8000 задает порт. Если порт не указать, по умолчанию используется 8000.
Типичная ошибка: если порт занят, выводится сообщение "Address already in use". Решение: выбрать другой порт или завершить процесс, занимающий порт, командой kill в Linux или через диспетчер задач в Windows.
Еще одна проблема: отсутствие модуля http.server в Python 2. Рекомендуется использовать Python 3.
Как сделать сервер доступным для других устройств в локальной сети?
По умолчанию сервер привязан к localhost (127.0.0.1). Чтобы разрешить доступ с других компьютеров, указывается параметр --bind 0.0.0.0:
python3 -m http.server 8000 --bind 0.0.0.0
После этого сервер слушает все сетевые интерфейсы. Доступ возможен по IP-адресу компьютера в локальной сети.
Возможная сложность: брандмауэр операционной системы может блокировать входящие соединения. Необходимо добавить правило для порта 8000.
Как изменить рабочую директорию сервера?
Вместо перехода в нужную папку можно указать путь через параметр --directory:
python3 -m http.server 8000 --directory /var/www/html
Этот параметр появился в Python 3.7. Для более старых версий нужно сначала сменить директорию командой cd.
Ошибка: если путь содержит пробелы, команда может быть неправильно интерпретирована. Рекомендуется заключать путь в кавычки.
Как включить поддержку CGI-скриптов?
Сервер может выполнять CGI-скрипты, если добавить флаг --cgi. Скрипты должны размещаться в поддиректории cgi-bin внутри рабочей папки:
python3 -m http.server --cgi 8000
Пример CGI-скрипта на Python:
#!/usr/bin/env python3
print("Content-Type: text/html")
print()
print("<h1>Hello from CGI!</h1>")
Частая проблема: скрипт не выполняется, а отображается его содержимое. Причина: отсутствуют права на выполнение (chmod +x) или неправильный shebang. На Windows CGI может не работать из-за особенностей системы.
Как создать собственный HTTP сервер на основе класса BaseHTTPRequestHandler?
Модуль http.server позволяет определить свой обработчик запросов. Пример сервера, возвращающего "Hello, World":
from http.server import HTTPServer, BaseHTTPRequestHandler
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b'<h1>Hello, World!</h1>')
server = HTTPServer(('localhost', 8080), MyHandler)
server.serve_forever()
Этот подход позволяет гибко управлять ответами, обрабатывать разные методы и пути.
Сложности: вручную нужно обрабатывать все детали протокола, включая заголовки, коды ошибок, многопоточность. Для простых задач достаточно встроенного SimpleHTTPRequestHandler.
Как использовать многопоточный сервер для одновременной обработки запросов?
Класс ThreadingHTTPServer из модуля http.server позволяет обслуживать несколько запросов параллельно. Пример:
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
server = ThreadingHTTPServer(('', 8000), SimpleHTTPRequestHandler)
print('Сервер запущен на порту 8000')
server.serve_forever()
Это полезно при тестировании, когда несколько клиентов одновременно обращаются к серверу.
Ограничение: для статических файлов многопоточность избыточна, но может быть полезна при медленных клиентах. В production рекомендуется использовать полноценные веб-серверы.
Как автоматически открыть браузер после запуска сервера?
Для удобства можно добавить вызов модуля webbrowser в скрипте. Пример:
import http.server
import socketserver
import webbrowser
import threading
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
t = threading.Thread(target=httpd.serve_forever)
t.daemon = True
t.start()
webbrowser.open(f'http://localhost:{PORT}')
t.join()
После запуска сервера браузер открывает стартовую страницу. Сервер работает до нажатия Ctrl+C.
Возможная проблема: если сервер не успел запуститься до открытия браузера, может появиться ошибка соединения. Рекомендуется небольшая задержка перед webbrowser.open.
Расширенные примеры настройки сервера
REST-подобный сервер с маршрутизацией
Пример обработчика, который распознает разные URL и возвращает JSON-ответы.
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
class RESTHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/api/status':
self.send_json({'status': 'OK', 'version': '1.0'})
elif self.path.startswith('/api/items/'):
item_id = self.path.split('/')[-1]
self.send_json({'id': item_id, 'name': f'Item {item_id}'})
else:
self.send_error(404, 'Not Found')
def send_json(self, data):
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(data).encode())
server = HTTPServer(('', 8080), RESTHandler)
print('REST сервер запущен на порту 8080')
server.serve_forever()
Результат: при запросе /api/status возвращается JSON {"status":"OK","version":"1.0"}. Для /api/items/42 получается {"id":"42","name":"Item 42"}.
Сервер с поддержкой POST-запросов для загрузки данных
Обработчик, который принимает POST-запросы и сохраняет тело запроса в файл.
import os
from http.server import HTTPServer, BaseHTTPRequestHandler
class UploadHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length)
filename = f'uploads/data_{len(os.listdir("uploads"))}.bin'
os.makedirs('uploads', exist_ok=True)
with open(filename, 'wb') as f:
f.write(body)
self.send_response(201)
self.send_header('Location', f'/{filename}')
self.end_headers()
self.wfile.write(b'Uploaded')
server = HTTPServer(('', 8001), UploadHandler)
print('Upload сервер на порту 8001')
server.serve_forever()
Для тестирования можно отправить POST-запрос через curl: curl -X POST --data "Hello" http://localhost:8001/. Файл сохранится в папку uploads.
Сервер с кастомной страницей ошибки 404
Модификация SimpleHTTPRequestHandler для замены стандартного сообщения об ошибке.
from http.server import SimpleHTTPRequestHandler, HTTPServer
class CustomErrorHandler(SimpleHTTPRequestHandler):
def send_error(self, code, message=None):
if code == 404:
self.send_response(404)
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write(b'<h1>Страница не найдена</h1><p>Попробуйте другую ссылку.</p>')
else:
super().send_error(code, message)
server = HTTPServer(('', 8002), CustomErrorHandler)
print('Сервер с кастомной 404 на порту 8002')
server.serve_forever()
При обращении к несуществующему пути отображается HTML-страница с сообщением на русском языке.
Сервер с фильтрацией файлов по расширению
Расширение SimpleHTTPRequestHandler, которое позволяет отдавать только файлы определенных типов.
from http.server import SimpleHTTPRequestHandler, HTTPServer
import os
class FilteredHandler(SimpleHTTPRequestHandler):
def list_directory(self, path):
# Переопределяем отображение директории, чтобы показывать только .html файлы
files = [f for f in os.listdir(path) if f.endswith('.html') or os.path.isdir(os.path.join(path, f))]
# Используем родительский метод для отображения списка
# Но можно написать свою HTML-страницу
return super().list_directory(path) # упрощение
server = HTTPServer(('', 8003), FilteredHandler)
server.serve_forever()
Данный пример упрощен; полная реализация требует замены шаблона листинга директории.
Сервер с самоподписанным SSL-сертификатом
Использование модуля ssl для шифрования трафика. Потребуется сгенерировать сертификат (например, с помощью openssl).
import ssl
from http.server import HTTPServer, SimpleHTTPRequestHandler
httpd = HTTPServer(('localhost', 4443), SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(httpd.socket,
keyfile='key.pem',
certfile='cert.pem',
server_side=True)
print('HTTPS сервер на порту 4443')
httpd.serve_forever()
Перед запуском необходимо создать файлы key.pem и cert.pem. Подключение происходит по протоколу HTTPS.