Выбрать главу
Разработка сервера  CAPTCHA

При использовании модулей, доступных в полном дистрибутиве Питона, задача создания сервера HTTP становится не такой уж пугающей, как может показаться. Наш сервер Captcha будет основан на классах, предоставленных модулем Питона BaseHTTPServer, так что мы начинаем с импорта этого модуля вместе с несколькими дополнительными модулями-утилитами:

import BaseHTTPServer

import re

import os

import shutil

Модуль BaseHTTPServer определяет два класса, которые вместе включают полную реализацию сервера HTTP. Класс BaseHTTPServer реализует основной сервер, который будет слушать поступающие HTTP-запросы на некотором сетевом порту, и мы используем этот класс, как есть.

При получении корректного HTTP-запроса BaseHTTPServer пошлет этот запрос обработчику запросов. Наша реализация такого обработчика запросов, основанная на BaseHTTPRequestHandler, довольно скудна, так как ожидается, что всё, что он будет делать - запрашивать поля GET и HEAD в форме captcha?text=abcd. Следовательно, всё мы должны сделать - переписать методы do_GET() и do_HEAD() базового класса.

От запроса HEAD ожидается возвращение только заголовков запрошенного объекта, а не содержимого, чтобы сохранять время, за которое содержимое не изменится со времени последнего запроса (что-то, что может быть определено проверкой заголовка Last-Modified). Мы игнорируем такую аккуратность; мы возвращаем заголовки именно тогда, когда мы получаем запрос HEAD, но мы, тем не менее, будем генерировать полностью новое изображение. Это в некоторой степени расточительно, но зато код будет простым. Если важна производительность, можно разработать другую реализацию.

Наша реализация начинается с определения метода do_GET(), который просто вызывает метод do_HEAD(), который будет генерировать вопрос Captcha и возвращать заголовки клиенту. do_GET(), впоследствии, копирует содержание файлового объекта, возвращённого методом do_HEAD() в выходной файл, такой как объект обработчика запроса (выделено), который в свою очередь возвращает это содержимое клиенту (например, браузеру):

class CaptchaRequestHandler(

         BaseHTTPServer.BaseHTTPRequestHandler):

   def do_GET(self):

      f=self.do_HEAD()

      shutil.copyfileobj(f,self.wfile)

      f.close()

Метод do_HEAD() сначала определяет, получили ли мы правильный запрос (то есть, URI в форме captcha?text=abcd), вызывая метод gettext() (выделено, определяется позже в коде). Если URI некорректен, метод gettext(), возвращает None и тогда do_HEAD() возвращает клиенту ошибку File not found (Файл не найден), вызывая метод send_error() базового класса:

   def do_HEAD(self):

      text=self.gettext()

      if text==None:

            self.send_error(404, "File not found")

            return None

Если был запрошен корректный URI, фактическое изображение генерируется методом captcha(), который возвращает имя файла сгенерированного изображения. Если этот метод терпит неудачу по любой причине, клиенту возвращается Internal server error (Внутренняя ошибка сервера):

      try:

            filename = self.captcha(text)

      except:

            self.send_error(500, "Internal server error")

            return None

Если все прошло хорошо, мы открываем файл изображения, отсылаем клиенту ответ 200 (показывающий успешную операцию), и возвращаем заголовок Content-type, устанавливающий, что мы возвращаем png-изображение. Затем мы используем функцию fstat() с номером handle открытого файла в качестве аргумента, чтобы извлечь длину сгенерированного изображения и вернуть её как заголовок  Content-Length (выделено), сопроводив временем модификации и пустой строкой, означающей конец заголовков перед возвратом открытого файлового объекта f:

      f = open(filename,'rb')

      self.send_response(200)

      self.send_header("Content-type", 'image/png')

      fs = os.fstat(f.fileno())

      self.send_header("Content-Length", str(fs[6]))

      self.send_header("Last-Modified",

                self.date_time_string(fs.st_mtime))