Рубрики
Без рубрики

http.server – Базовые классы для реализации веб-серверов

Автор оригинала: Doug Hellmann.

Цель:

http.server включает классы, которые могут составлять основу веб сервер.

http.server использует классы из socketserver для создания базовых классов для создания HTTP-серверов. HTTPServer можно использовать напрямую, но BaseHTTPRequestHandler предназначен для расширения для обработки каждого метода протокола (GET, POST и т. д.).

HTTP GET

Чтобы добавить поддержку метода HTTP в класс обработчика запросов, реализуйте метод do_METHOD () , заменив METHOD на имя метода HTTP (например, do_GET () , do_POST () и т. д.). Для согласованности методы обработчика запросов не принимают аргументов. Все параметры запроса анализируются BaseHTTPRequestHandler и сохраняются как атрибуты экземпляра экземпляра запроса.

В этом примере обработчика запросов показано, как вернуть ответ клиенту, а также некоторые локальные атрибуты, которые могут быть полезны при построении ответа.

http_server_GET.py

from http.server import BaseHTTPRequestHandler
from urllib import parse


class GetHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        parsed_path  parse.urlparse(self.path)
        message_parts  [
            'CLIENT VALUES:',
            {} ({})'.format(
                self.client_address,
                self.address_string()),
            {}'.format(self.command),
            {}'.format(self.path),
            'real>{}'.format(parsed_path.path),
            {}'.format(parsed_path.query),
            {}'.format(self.request_version),
            '',
            'SERVER VALUES:',
            {}'.format(self.server_version),
            {}'.format(self.sys_version),
            {}'.format(self.protocol_version),
            '',
            'HEADERS RECEIVED:',
        ]
        for name, value in sorted(self.headers.items()):
            message_parts.append(
                '{}{}'.format(name, value.rstrip())
            )
        message_parts.append('')
        message  '\r\n'.join(message_parts)
        self.send_response(200)
        self.send_header('Content-Type',
                         'text/plain;)
        self.end_headers()
        self.wfile.write(message.encode('utf-8'))


if __name__  '__main__':
    from http.server import HTTPServer
    server  HTTPServer(('localhost', 8080), GetHandler)
    print('Starting server, use  to stop')
    server.serve_forever()

Текст сообщения собирается и затем записывается в wfile , дескриптор файла, охватывающий сокет ответа. Для каждого ответа требуется код ответа, установленный с помощью send_response () . Если используется код ошибки (404, 501 и т. Д.), Соответствующее сообщение об ошибке по умолчанию включается в заголовок, или сообщение может быть передано с кодом ошибки.

Чтобы запустить обработчик запросов на сервере, передайте его конструктору HTTPServer , как в части обработки __main__ в примере сценария.

Затем запустите сервер:

$ python3 http_server_GET.py

Starting server, use  to stop

В отдельном терминале используйте curl для доступа к нему:

$ curl -v -i

*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET
> Host: 127.0.0.1:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
HTTP/1.0 200 OK
Content-Type: text/plain;
Server: BaseHTTP/0.6 Python/3.5.2
Date: Thu, 06 Oct 2016 20:44:11 GMT

CLIENT VALUES:
client_address=('127.0.0.1', 52934) (127.0.0.1)
real path=/

SERVER VALUES:

HEADERS RECEIVED:
* Connection #0 to host 127.0.0.1 left intact

Примечание

Вывод, производимый разными версиями curl , может отличаться. Если выполнение примеров дает другой результат, проверьте номер версии, указанный curl .

HTTP POST

Поддержка запросов POST – это немного больше работы, потому что базовый класс не анализирует данные формы автоматически. Модуль cgi предоставляет класс FieldStorage , который знает, как анализировать форму, если ей заданы правильные входные данные.

http_server_POST.py

import cgi
from http.server import BaseHTTPRequestHandler
import io


class PostHandler(BaseHTTPRequestHandler):

    def do_POST(self):
        # Parse the form data posted
        form  cgi.FieldStorage(
            fpself.rfile,
            headersself.headers,
            environ{
                'REQUEST_METHOD': 'POST',
                'CONTENT_TYPE': self.headers['Content-Type'],
            }
        )

        # Begin the response
        self.send_response(200)
        self.send_header('Content-Type',
                         'text/plain;)
        self.end_headers()

        out  io.TextIOWrapper(
            self.wfile,
            encoding'utf-8',
            line_bufferingFalse,
            write_throughTrue,
        )

        out.write('Client: {}\n'.format(self.client_address))
        out.write('User-agent: {}\n'.format(
            self.headers['user-agent']))
        out.write('Path: {}\n'.format(self.path))
        out.write('Form data:\n')

        # Echo back information about what was posted in the form
        for field in form.keys():
            field_item  form[field]
            if field_item.filename:
                # The field contains an uploaded file
                file_data  field_item.file.read()
                file_len  len(file_data)
                del file_data
                out.write(
                    '\tUploaded {} as {!r} ({} bytes)\n'.format(
                        field, field_item.filename, file_len)
                )
            else:
                # Regular form value
                out.write('\t{}{}\n'.format(
                    field, form[field].value))

        # Disconnect our encoding wrapper from the underlying
        # buffer so that deleting the wrapper doesn't close
        # the socket, which is still being used by the server.
        out.detach()


if __name__  '__main__':
    from http.server import HTTPServer
    server  HTTPServer(('localhost', 8080), PostHandler)
    print('Starting server, use  to stop')
    server.serve_forever()

Запускаем сервер в одном окне:

$ python3 http_server_POST.py

Starting server, use  to stop

Аргументы для curl могут включать данные формы, которые будут отправлены на сервер с помощью параметра -F . Последний аргумент, -F , отправляет содержимое файла http_server_GET.py для иллюстрации чтения данных файла из формы.

$ curl -v http://127.0.0.1:8080/ -F
-F

*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST / HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Length: 1974
> Expect: 100-continue
> Content-Type: multipart/form-data;
>
* Done waiting for 100-continue
HTTP/1.0 200 OK
Content-Type: text/plain;
Server: BaseHTTP/0.6 Python/3.5.2
Date: Thu, 06 Oct 2016 20:53:48 GMT

Client: ('127.0.0.1', 53121)
User-agent: curl/7.43.0
Path: /
Form data:
   
    Uploaded datafile as 'http_server_GET.py' (1612 bytes)
   
* Connection #0 to host 127.0.0.1 left intact

Заправка и разветвление

HTTPServer – это простой подкласс socketserver.TCPServer , который не использует несколько потоков или процессов для обработки запросов. Чтобы добавить потоки или разветвление, создайте новый класс, используя соответствующее соединение с сервера сокетов.

http_server_threads.py

from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
import threading


class Handler(BaseHTTPRequestHandler):

    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-Type',
                         'text/plain;)
        self.end_headers()
        message  threading.currentThread().getName()
        self.wfile.write(message.encode('utf-8'))
        self.wfile.write(b'\n')


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""


if __name__  '__main__':
    server  ThreadedHTTPServer(('localhost', 8080), Handler)
    print('Starting server, use  to stop')
    server.serve_forever()

Запустите сервер так же, как в других примерах.

$ python3 http_server_threads.py

Starting server, use  to stop

Каждый раз, когда сервер получает запрос, он запускает новый поток или процесс для его обработки:

$ curl http://127.0.0.1:8080/

Thread-1

$ curl http://127.0.0.1:8080/

Thread-2

$ curl http://127.0.0.1:8080/

Thread-3

Замена ForkingMixIn на ThreadingMixIn приведет к аналогичным результатам, используя отдельные процессы вместо потоков.

Обработка ошибок

Обрабатывайте ошибки, вызывая send_error () , передавая соответствующий код ошибки и необязательное сообщение об ошибке. Полный ответ (с заголовками, кодом состояния и телом) создается автоматически.

http_server_errors.py

from http.server import BaseHTTPRequestHandler


class ErrorHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        self.send_error(404)


if __name__  '__main__':
    from http.server import HTTPServer
    server  HTTPServer(('localhost', 8080), ErrorHandler)
    print('Starting server, use  to stop')
    server.serve_forever()

В этом случае всегда возвращается ошибка 404.

$ python3 http_server_errors.py

Starting server, use  to stop

Сообщение об ошибке передается клиенту с использованием HTML-документа, а также заголовка для указания кода ошибки.

$ curl -i http://127.0.0.1:8080/

HTTP/1.0 404 Not Found
Server: BaseHTTP/0.6 Python/3.5.2
Date: Thu, 06 Oct 2016 20:58:08 GMT
Connection: close
Content-Type:
Content-Length: 447




    
        
        Error response
    
    
        

Error response

Error code: 404

Message: Not Found.

Error code explanation: 404 - Nothing matches the given URI.

Установка заголовков

Метод send_header добавляет данные заголовка в ответ HTTP. Он принимает два аргумента: имя заголовка и значение.

http_server_send_header.py

from http.server import BaseHTTPRequestHandler
import time


class GetHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        self.send_response(200)
        self.send_header(
            'Content-Type',
            'text/plain;,
        )
        self.send_header(
            'Last-Modified',
            self.date_time_string(time.time())
        )
        self.end_headers()
        self.wfile.write('Response body\n'.encode('utf-8'))


if __name__  '__main__':
    from http.server import HTTPServer
    server  HTTPServer(('localhost', 8080), GetHandler)
    print('Starting server, use  to stop')
    server.serve_forever()

В этом примере в заголовке Last-Modified устанавливается текущая отметка времени, отформатированная в соответствии с RFC 7231.

$ curl -i http://127.0.0.1:8080/

HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.5.2
Date: Thu, 06 Oct 2016 21:00:54 GMT
Content-Type: text/plain;
Last-Modified: Thu, 06 Oct 2016 21:00:54 GMT

Response body

Сервер записывает запрос в терминал, как и в других примерах.

$ python3 http_server_send_header.py

Starting server, use  to stop
127.0.0.1 - - [06/Oct/2016 17:00:54] "GET / HTTP/1.1" 200 -

Использование командной строки

http.server включает встроенный сервер для обслуживания файлов из локальной файловой системы. Запустите его из командной строки с помощью параметра -m для интерпретатора Python.

$ python3 -m http.server 8080

Serving HTTP on 0.0.0.0 port 8080 ...
127.0.0.1 - - [06/Oct/2016 17:12:48] "HEAD /index.rst HTTP/1.1" 200 -

Корневой каталог сервера – это рабочий каталог, в котором запущен сервер.

$ curl -I http://127.0.0.1:8080/index.rst

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.5.2
Date: Thu, 06 Oct 2016 21:12:48 GMT
Content-type: application/octet-stream
Content-Length: 8285
Last-Modified: Thu, 06 Oct 2016 21:12:10 GMT

Смотрите также

  • стандартная библиотечная документация для http.server
  • socketserver – модуль socketserver предоставляет базовый класс, который обрабатывает необработанное соединение сокета.
  • RFC 7231 – «Протокол передачи гипертекста (HTTP/1.1): семантика и содержание» включает спецификацию формата заголовков и дат HTTP.