Модуль http.server в Python
Модуль http.server определяет классы для реализации HTTP-серверов (веб-серверов).
Предупреждение модуль http.server не рекомендуется для использования в продакшне, так как он выполняет только базовые проверки безопасности.
Класс http.server.HTTPServer , является подклассом socketserver.TCPServer() . Он создает и прослушивает HTTP-сокет, отправляя запросы обработчику.
Код для создания и запуска сервера выглядит так:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler): server_address = ('', 8000) httpd = server_class(server_address, handler_class) httpd.serve_forever()
Классы HTTP веб-серверов.
http.server.HTTPServer(server_address, RequestHandlerClass) :
Класс HTTPServer основан на классе TCPServer, сохраняя адрес сервера в виде переменных экземпляра с именами server_name и server_port .
Сервер доступен обработчику, обычно через переменную экземпляра сервера обработчика RequestHandlerClass .
http.server.ThreadingHTTPServer(server_address, RequestHandlerClass) :
Класс ThreadingHTTPServer идентичен HTTPServer , но использует потоки для обработки запросов с помощью миксина ThreadingMixIn .
Такое поведение полезно для обработки запросов веб-браузеров, предварительно открывающих сокеты, на которых HTTPServer будет ждать бесконечно.
Классы HTTPServer и ThreadingHTTPServer должны получить в качестве аргумента RequestHandlerClass при создании экземпляра, из которых этот модуль предоставляет три различных варианта.
Обработчики HTTP-запросов:
- http.server.BaseHTTPRequestHandler() — класс используется для обработки HTTP-запросов, поступающих на сервер;
- http.server.SimpleHTTPRequestHandler() — обслуживает файлы из текущего каталога, напрямую сопоставляя структуру каталогов с HTTP-запросами;
- http.server.CGIHTTPRequestHandler() — обслуживает файлы или вывод сценариев CGI из текущего каталога.
Примеры создания простых HTTP web-серверов
Пример HTTP web-сервера, обрабатывающего статические файлы.
python3 -m http.server 8000 --bind 127.0.0.1
Если запустить HTTP сервер таким способом, то можно создать, например, фиктивный API путем загрузки на этот сервер статических файлов json или/и xml . Структура ресурсов файлов, организованных в подпапки, будет обеспечивать RESTful-подобные URL-адреса. Например, /users/all.json может содержать фиктивные записи пользователей. Этот подход еще быстрее, чем создание, например, приложения Flask . База данных не требуется, работает везде. Можно применять там, где например есть некоторые трудности с командой терминала scp для загрузки данных. Решение — можно запустить простой сервер на удаленной машине и загрузить необходимое содержимое через HTTP.
Пример HTTP web-сервера, с поддержкой SSL.
Для запуска защищенного HTTPs сервера создайте следующий модуль:
from http.server import HTTPServer, BaseHTTPRequestHandler import ssl httpd = HTTPServer(('localhost', 4443), BaseHTTPRequestHandler) httpd.socket = ssl.wrap_socket (httpd.socket, keyfile="path/to/key.pem", certfile='path/to/cert.pem', server_side=True) httpd.serve_forever()
Для создания файлов ключей и сертификатов с помощью OpenSSL используйте следующую команду
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
Пример HTTP web-сервера, обрабатывающего запросы GET и POST.
Это очень простой пример HTTP-сервера, который отвечает Hello, world! на запросы. Обратите внимание, что self.send_response(200) и self.end_headers() являются обязательными, в противном случае ответ не будет считаться действительным. Проверить работу сервера можно при помощи утилиты терминала curl .
from http.server import HTTPServer, BaseHTTPRequestHandler class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): # определяем метод `do_GET` def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(b'Hello, world!') httpd = HTTPServer(('localhost', 8000), SimpleHTTPRequestHandler) httpd.serve_forever()
Обратите внимание, что self.wfile является файлоподобным объектом, поэтому ожидает байтоподобных объектов для функции записи. Другой способ передать информацию в self.wfile — это использование объекта io.BytesIO .
from http.server import HTTPServer, BaseHTTPRequestHandler from io import BytesIO class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): # определяем метод `do_GET` def do_GET(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(b'Hello, world!') # определяем метод `do_POST` def do_POST(self): content_length = int(self.headers['Content-Length']) body = self.rfile.read(content_length) self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() response = BytesIO() response.write(b'This is POST request. ') response.write(b'Received: ') response.write(body) self.wfile.write(response.getvalue()) httpd = HTTPServer(('localhost', 8000), SimpleHTTPRequestHandler) httpd.serve_forever()
К телу запроса можно получить доступ через self.rfile . Это BufferedReader , поэтому для получения содержимого должен быть выполнен метод .read([size]) . Обратите внимание, что функции должен быть явно передан размер, иначе запрос будет висеть и никогда не закончится.
Вот почему получение content_length необходимо. Он может быть извлечен через self.headers и преобразован в целое число. Пример выше просто печатает обратно все, что он получает.
Как завершить работу сервера Python?
Если сервер запускается через интерпретатор Python, например python3 myserver.py, просто нажмите Crtl + C .
Если необходимо элегантно закрыть его с помощью кода, то надо создать какое-либо условие, или точку, или исключение, для того, чтобы вызвать команду на выключение. Например можете добавить блок и вызвать httpd.shutdown() — так как сам класс HTTPServer является подклассом SocketServer.TCPServer . В свою очередь класс SocketServer.TCPServer является подклассом SocketServer.BaseServer у которого есть метод BaseServer.shutdown() .
Из вышесказанного можно заключить то, что класс HttpServer так же будет имеет метод .shutdown() , как унаследованный от класса SocketServer.BaseServer .
import os from http.server import SimpleHTTPRequestHandler, HTTPServer os.chdir('c:/users/owner/desktop/pyserver') server_address = ('', 8000) try: httpd = HTTPServer(server_address, SimpleHTTPRequestHandler) httpd.serve_forever() except Exception: httpd.shutdown()
Метод httpd.shutdown() говорит серверу, что бы цикл httpd.serve_forever() остановился, при этом дождался завершения обработки запросов подключенных клиентов.
- КРАТКИЙ ОБЗОР МАТЕРИАЛА.
- Класс BaseHTTPRequestHandler() модуля http.server
- Класс SimpleHTTPRequestHandler() модуля http.server
- Класс CGIHTTPRequestHandler() модуля http.server
Простой http сервер.¶
Любой online-сервис начинается с того, что необходимо приложение, которое способно принимать и обрабатывать http-запросы.
В python есть встроенный модуль, который полвзоляет создавать простой http-сервер.
from http.server import BaseHTTPRequestHandler from http.server import HTTPServer def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler): server_address = ('', 8000) httpd = server_class(server_address, handler_class) try: httpd.serve_forever() except KeyboardInterrupt: httpd.server_close()
Если запустить функцию run() , то будет запущен http-сервер, и если в браузере Вы введете 127.0.0.1:8000 , то Вам вернутся ответ, но ответ будет содержать ошибку, потому что сервер не знает как обрабатывать запросы.
Для того что бы исправить эту ошибку трубется в обработчике( BaseHTTPRequestHandler ) реализовать метод по обработке GET-запросов:
class HttpGetHandler(BaseHTTPRequestHandler): """Обработчик с реализованным методом do_GET.""" def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(''.encode()) self.wfile.write('Простой HTTP-сервер. '.encode()) self.wfile.write('Был получен GET-запрос.'.encode())
Теперь, если запустить сервер с новым обработчиком run(handler_class=HttpGetHandler) , то при переходе по адресу 127.0.0.1:8000 можно будет увидеть ответ на запрос.
Обратите внимание, что при формировании ответа обработчика был использован метод encode() у строк. Сделано этого потому что в теле ответа могут быть использованы только байтовые строки.
Задания¶
- Ознакомьтесь с описанием протокола HTTP.
- Дополните обработчик запросов таким образом, что бы он мог обрабатывать запросы по методам POST, PUT и HEAD. Для проверки реализации используйте библиотку requests.
© Copyright Revision d00c0df4 .
Built with Sphinx using a theme provided by Read the Docs.
Read the Docs v: latest
Versions latest Downloads html On Read the Docs Project Home Builds Free document hosting provided by Read the Docs.
Встроенный HTTP-сервер Python
А вы знали, что у Python-а есть HTTP-сервер, встроенный в стандартную библиотеку? Он очень удобен для предварительного просмотра сайтов.
# Для запуска сервера в текущем каталоге: # Python 3.x $ python3 -m http.server # Python 2.x $ python -m SimpleHTTPServer 8000
Запустив сервер таким образом, вы сможете просмотреть содержимое каталога по адресу localhost: 8000 .
Если у вас уже что-то работает на 8000 порту, можно выбрать другой порт, например:
# Python 3.x $ python3 -m http.server 7800 # Python 2.x $ python -m SimpleHTTPServer 7800
После этого вы сможете получить доступ к своему каталогу по адресу localhost:7800 .
Пишем простой сервер на Python
Ну, начнем как и везде с определений, берите тетрадь и ручку сейчас начнется нудятина. Чтобы мы cмогли написать свой сервер, нужно для начала понимать как он вообще работает, ловите определение:
Сервер – это программное обеспечение, которое ожидает запросов клиентов и обслуживает или обрабатывает их соответственно.
Если объяснять это своими словами, представьте фургон с хот-догами(сервер), проголодавшись, вы(клиент) подходите и говорите повару, что вы хотите заказать(запрос), после чего повар обрабатывает, что вы ему сказали и начинает готовить, в конечном итоге вы получаете свой хот-дог(результат) и сытый радуетесь жизни. Для наглядности посмотри схему.
Околопрактика
Для написания сервера мы будем использовать Python и модуль Socket.
Socket позволяет нам общаться с сервером с помощью сокетов. Весь код я постараюсь пояснять, дабы ты мой дорогой читатель все понял. В конце статьи будет готовый код.
Создайте два файла в одной директории:
Практика
Пишем код для серверной части, так что открывайте файл socket_server.py.
Начнем с импорта модуля и создания TCP-сокета:
import socket
Далее весь код будет с комментариями:
s.bind(('localhost', 3030)) # Привязываем серверный сокет к localhost и 3030 порту. s.listen(1) # Начинаем прослушивать входящие соединения conn, addr = s.accept() # Метод который принимает входящее соединение.
Добавим вечный цикл, который будет считывать данные с клиентской части, и отправлять их обратно.
while True: # Создаем вечный цикл. data = conn.recv(1024) # Получаем данные из сокета. if not data: break conn.sendall(data) # Отправляем данные в сокет. print(data.decode('utf-8')) # Выводим информацию на печать. conn.close()
Переходим к клиентской части, весь код теперь пишем в файле socket_client.py.
Начало у клиентской части такое-же как и у серверной.
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Далее подключимся к нашему серверу и отправим сообщение «Hello. Habr!».
s.connect(('localhost', 3030)) # Подключаемся к нашему серверу. s.sendall('Hello, Habr!'.encode('utf-8')) # Отправляем фразу. data = s.recv(1024) #Получаем данные из сокета. s.close()

Заключение
Вот мы с вами и написали свой первый сервер, рад был стараться для вас, ниже будет готовый код.
socket_server.py:
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('localhost', 3030)) # Привязываем серверный сокет к localhost и 3030 порту. s.listen(1) # Начинаем прослушивать входящие соединения. conn, addr = s.accept() # Метод который принимает входящее соединение. while True: data = conn.recv(1024) # Получаем данные из сокета. if not data: break conn.sendall(data) # Отправляем данные в сокет. print(data.decode('utf-8')) conn.close()
socket_client.py:
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('localhost', 3030)) # Подключаемся к нашему серверу. s.sendall('Hello, Habr!'.encode('utf-8')) # Отправляем фразу. data = s.recv(1024) #Получаем данные из сокета. s.close()
