Hunchentoot

Материал из wiki.lissyara.su
Перейти к: навигация, поиск

Содержание

HUNCHENTOOT - Веб сервер языка программирования Common Lisp, также известный как TBNL

Общая информация

Hunchentoot - это web сервер, написанный на языке программирования Common Lisp, и одновременно набор инструментов для создания динамических web страниц. Как автономный web сервер, Hunchentoot совместим с HTTP/1.1, поддерживает возобновляемые соединения (keep-alive) и SSL.

Hunchentoot поддерживает такие вещи как: обработка автоматических сессий (с cookies и без них), ведение протоколов событий, изменяемую обработку ошибок и простой доступ к GET и POST параметрам, отправляемыми клиентом. Hunchentoot не включает поддержку автоматической генерации HTML вывода. Для этих задач Вы можете использовать отдельно подключаемые библиотеки, такие как CL-WHO или HTML-TEMPLATE.

Hunchentoot разговаривает со своим фронт-ендом или с клиентом через TCP/IP сокеты и при желании использует многозадачность для обработки нескольких запросов в одно время. Поэтому, Hunchentoot невозможно полностью реализовать на переносимом Common Lisp. В "родном" режиме Hunchentoot работает с LispWorks (которая является главной платформой для разработки и тестирования) и в дополнение на всех Lisp'ах, которые поддерживают слои совместимости usocket и нити Bordeaux.

Hunchentoot поставляется с BSD-подобной лицензией, таким образом, вы можете использовать его где хотите.

Для примера, Hunchentoot используется (или использовался) в QuickHoney, Postabon, City Farming, Trip Planner, Jalat, Heike Stephan, xOs и в NIST.

Ссылка для скачивания: http://weitz.de/files/hunchentoot.tar.gz.

Скачивание и установка

Hunchentoot зависит от нескольких других Lisp библиотек, которые нужно установить первыми:

  • MD5 автор: Пьер Р. Май (Pierre R. Mai),
  • CL-BASE64 автор: Кевин Розенберг (Kevin Rosenberg),
  • RFC2388 автор: Дженис Дзеринс (Janis Dzerins),
  • CL-FAD автор: Питер Сибель (Peter Seibel),
  • trivial-backtrace автор: Гари Кинг (Gary King),
  • usocket автор: Эрик Хьюлсманн (Erik Huelsmann) (не нужно, если Вы используете LispWorks),
  • Bordeaux Threads автор: Грег Пфейл (Greg Pfeil) (не нужно, если Вы используете LispWorks),
  • CL+SSL автор: Дэвид Лихтеблау (David Lichteblau) (не нужно, если Вы используете LispWorks),
  • и мой собственный FLEXI-STREAMS (0.12.0 или выше), Chunga (1.0.0 или выше) и CL-PPCRE (плюс CL-WHO, (пример здесь) и Drakma для тестов).

Убедитесь, что используете последние версии всех этих библиотек (которые в свою очередь могут зависеть от других библиотек) - пробуйте использовать версии из репозитория, если Вы сомневаетесь в свежести библиотек. Помните: Вы можете компилировать Hunchentoot без поддержки SSL - таким образом отпадает надобность в CL+SSL - если Вы добавите :HUNCHENTOOT-NO-SSL в *СВОЙСТВАХ* (*FEATURES*) до компиляции.

Hunchentoot работает только с теми реализациями Lisp, в которых все коды символов Latin-1 совпадают с их отображением в Unicode (насколько Я знаю, это относится ко всем реализациям Lisp).

Hunchentoot поставляется вместе с документацией, которая может быть отдельно скачана с http://weitz.de/files/hunchentoot.tar.gz. Текущая версия 1.2.2.

Основной метод компилирования и установки Hunchentoot - это ASDF. Если Вы не хотите скачивать и устанавливать все зависимости вручную, попробуйте замечательную систему Quicklisp за авторством Зака Бина (Zach Beane).

Также Hunchentoot и его зависимости можно установить с помощью ASDF-INSTALL, clduild или мой собственный Starter Pack. Также есть порт для Gentoo Linux, спасибо Мэттью Кеннеди (Matthew Kennedy).

Текущую разрабатываемую версию Hunchentoot можно найти на https://github.com/edicl/hunchentoot. Если Вы хотите прислать патчи, пожалуйста сделайте форк репозитория с github и пришлите pull запросы.

Луис Оливеира (Luís Oliveira) разрабатывает не официальный darcs репозиторий Hunchentoot на http://common-lisp.net/~loliveira/ediware/.

Запуск Hunchentoot на 80-ом порту

Hunchentoot не поставляется с кодом запускающимся на привелегированном порте (то есть, порт 80 или 443) Unix-подобных операционных системах. Современные Unix-подобные системы имеют специфичный, не портируемый путь доступа не-root пользователей к привилегированным портам, поэтому этот функционал в Hunchentoot был сочтён ненужным. За дополнительной информацией обращайтесь к онлайн документации. На момент написания этого текста, документация YAWS содержит полную информацию по этому вопросу.

Hunchentoot за прокси

Если Вы хотите защитить Hunchentoot от прямого воздействия дикого, дикого Интернета или если Ваш Lisp web приложение является частью большого web сайта, то Вы можете спрятать Hunchentoot за прокси сервером. Есть один подход, который Я использую время от времени - модуль Apache mod_proxy с конфигурацией подобной этой:

ProxyPass /hunchentoot http://127.0.0.1:3000/hunchentoot
ProxyPassReverse /hunchentoot http://127.0.0.1:3000/hunchentoot

Создаётся туннель для всех запросов, где URI пути начинаются с "/hunchentoot" для (Hunchentoot) сервера, слушающего 3000-ый порт на той-же машине.

Конечно, есть и другие (более легковесные) web прокси, которые Вы можете использовать взамен Apache.

Поддержка и списки рассылки

Для вопросов, отчётов об ошибках, запросов о внедрении новых свойств, улучшений или исправлений, пожалуйста, используйте список рассылки tbnl-devel. Если Вы хотите получать уведомления о будущих выпусках подпишитесь на список рассылки tbnl-announce. За создание этого списка рассылки - спасибо службам common-lisp.net. Вы можете найти списки рассылки разработчиков здесь (спасибо Tiarnán Ó Corráin).

Если Вы хотите прислать исправления, пожалуйста прочтите вначале это.

Ваш собственный Web сервер (простая версия подростков Нью-Йорка)

Запустить Ваш персональный web сервер очень просто. Выполните что-то подобное этому:

(hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242))

Это всё. Теперь Вы можете внести адрес "http://127.0.0.1:4242" в Ваш браузер и увидите что-нибудь, хотя это не очень интересно.

По умолчанию, Hunchentoot обслуживает файлы из директории www/ в его дереве исходников. В общей поставке эта директория содержит HTML версию документации и шаблоны ошибок. Расположение корневой директории документации можно определить при создании нового ACCEPTOR экземпляра с помощью ACCEPTOR-DOCUMENT-ROOT. Также, расположение директории шаблона ошибки можно определить с помощью ACCEPTOR-ERROR-TEMPLATE-DIRECTORY.

Класс EASY-ACCEPTOR реализовывает каркас для разработки web приложений. Обработчики объявляются с помощью использования макроса DEFINE-EASY-HANDLER. Запрос обрабатывается с помощью обращения к списку управляющих функций в *DISPATCH-TABLE*. Каждая из функций в этом списке вызывается для определения места обработки запроса, переданного в виде единственного аргумента. Если управляющая функция будет обрабатывать запрос, она вернёт другую функцию для создания желаемой страницы.

DEFINE-EASY-HANDLER состоит из набора функций, которые реализуют управление, и используемых для работы со стандартными задачами. Задокументировано в главе о простых обработчиках

Пришло время для экспериментов, попробуйте следующий код

(hunchentoot:define-easy-handler (say-yo :uri "/yo") (name)
  (setf (hunchentoot:content-type*) "text/plain")
  (format nil "Hey~@[ ~A~]!" name))

и увидите что получится по ссылке "http://127.0.0.1:4242/yo" или "http://127.0.0.1:4242/yo?name=Dude" .

Hunchentoot поставляется с маленьким демонстрационным web сайтом, иллюстрирующим возможности Hunchentoot и служащим для проверки работоспособности web сервера. Для запуска демонстрационного web сайта, вставьте следующий код в Ваш Repl:

(asdf:oos 'asdf:load-op :hunchentoot-test)

Теперь пройдите по ссылке "http://127.0.0.1:4242/hunchentoot/test" и поиграйтесь.

Руководства и дополнения

Здесь перечислены некоторые руководства по Hunchentoot:

Проверяйте даты этих руководств! Многие из них могут не правильно работать с последним релизом Hunchentoot, так как произошли некоторые изменения в API, особенно в 2009 году. Факт того, что эти статьи перечислены здесь не говорит о том, что Я полностью согласен с их идеями и кодом. Вам самим решать подходят для Вас эти сведения или нет.

А здесь список программ расширяющих возможности Hunchentoot или базирующихся на нём:

  • Каркас web приложений web4r. Автор: Томо Матсумото (Tomo Matsumoto)
  • Weblocks - "continuation-подобный web каркас" на базе Hunchentoot. Автор: Слава Акмечет (Slava Akhmechet).
  • HT-AJAX - каркас Ajax для Hunchentoot. Автор: Ури Маршак (Ury Маршак).
  • HT-SIMPLE-AJAX - упрощённая версия HT-AJAX.
  • Мак Чэн (Mac Chan) портировал Lisp Server Pages за авторством Джона Вайсмана (John Wiseman) на Hunchentoot.
  • hunchentoot-dir-lister - программа сканирующая директорию на наличие дополнений для Hunchentoot. Автор: Дмитрий Лиотев (Dimitre Liotev).
  • nuclblog - блоговый движок, использующий Hunchentoot. Автор: Сайрус Хармон (Cyrus Harmon).
  • hunchentoot-cgi - обеспечивает обработку CGI для Hunchentoot. Автор: Сайрус Хармон (Cyrus Harmon). hunchentoot-cgi (автор: Сайрус Хармон (Cyrus Harmon)) обеспечивает управление CGI для Hunchentoot.
  • CL-WEBDAV - сервер WebDAV на основе Hunchentoot.
  • RESTAS - web каркас, основанный на Hunchentoot.

Справка по функциям и переменным

Приёмники (Acceptors)

Если Вы хотите заставить Hunchentoot делать что-то полезное, то в начале Вы должны создать и запустить acceptor (приёмник). Также возможно запустить несколько acceptor'ов, каждый из которых будет слушать разные порты, в одном образе.

acceptor

[Стандартный класс]

Для создания web сервера Hunchentoot, Вы создаёте экземпляр или один подкласс этого класса и используете функцию общего назначения START для запуска экземпляра (и STOP для остановки экземпляра). Используйте инициализирующий аргумент :port в том случае, если Вы не хотите прослушивать стандартный http порт 80. Существуют и другие инициализирующие аргументы, большинство которых редко используются. Они в деталях описаны в соответствующем разделе этого класса.
Однако, если Вы работаете с Lisp без возможности MP, Вы можете работать одновременно с несколькими активными ACCEPTOR экземплярами (слушающими различные порты) одновременно.

ssl-acceptor

[Стандартный класс]

Создайте и запустите экземпляр этого класса (взамен ACCEPTOR), если Вы хотите https сервер. Есть два обязательных инициализирующих аргумента, :SSL-CERTIFICATE-FILE и :SSL-PRIVATEKEY-FILE, для обозначения путей раскомментируйте файл сертификата и файл ключа в PEM формате. В LispWorks, Вы можете хранить эти два файла в одном в этом случае второй инициализирующий аргумент опционален. Вы можете использовать инициализирующий аргумент :SSL-PRIVATEKEY-PASSWORD для указания пароля (в виде строки) для файла ключа (или NIL, по умолчанию, без пароля).
Портом по умолчанию для SSL-ACCEPTOR является 443 взамен 80.

start acceptor => acceptor

[Общая функция]

Запускает acceptor, после чего начинается приём соединений. Возвращает acceptor.

stop acceptor => acceptor

[Общая функция]

Acceptor останавливается, приём запросов прекращается.

*acceptor*

[Специальная переменная]

Текущий ACCEPTOR объект в контексте запроса.

acceptor-address acceptor => address

acceptor-port acceptor => port

acceptor-read-timeout acceptor => read-timeout

acceptor-ssl-certificate-file ssl-acceptor => ssl-certificate-file

acceptor-ssl-privatekey-file ssl-acceptor => ssl-privatekey-file

acceptor-ssl-privatekey-password ssl-acceptor => ssl-privatekey-password

acceptor-ssl-certificate-file listen-backlog => number-of-pending-connections

acceptor-write-timeout acceptor => write-timeout

[Общие считыватели]

Число ожидаемых соединений принимаемых слушающим сокетом до того, как ядро будет отбрасывать другие входящие соединения. Не работает на LispWorks. Перечислены приёмники для различных слотов ACCEPTOR объектов (очевидно, что многие из них имеют смысл для SSL-ACCEPTOR объектов). Для дополнительной информации, смотрите документацию по соответствующим инициализирующим аргументам.

acceptor-access-log-destination acceptor => (or pathname null)

(setf (acceptor-access-log-destination acceptor ) new-value)

acceptor-document-root acceptor => (or pathname null)

(setf (acceptor-document-root acceptor ) new-value)

acceptor-error-template-directory acceptor => (or pathname null)

(setf (acceptor-error-template-directory acceptor ) new-value)

acceptor-input-chunking-p acceptor => input-chunking-p

(setf (acceptor-input-chunking-p acceptor ) new-value)

acceptor-message-log-destination acceptor => (or pathname null)

(setf (acceptor-message-log-destination acceptor ) new-value)

acceptor-name acceptor => name

(setf (acceptor-name acceptor ) new-value)

acceptor-output-chunking-p acceptor => output-chunking-p

(setf (acceptor-output-chunking-p acceptor ) new-value)

acceptor-persistent-connections-p acceptor => persistent-connections-p

(setf (acceptor-persistent-connections-p acceptor ) new-value)

acceptor-reply-class acceptor => reply-class

(setf (acceptor-reply-class acceptor ) new-value)

acceptor-request-class acceptor => request-class

(setf (acceptor-request-class acceptor ) new-value)

[Общие получатели доступа]

Эти получатели доступа предназначены для различных слотов ACCEPTOR объектов. Для получения подробной документации смотрите документацию по соответствующим инициализирующим аргументам.

acceptor-ssl-p acceptor => generalized-boolean

[Общая функция]

Возвращает значение true если acceptor использует SSL соединение. По-умолчанию возвращает NIL, подклассы ACCEPTOR могут использовать этот метод для того, чтобы сигнализировать об использовании безопасных соединений - смотрите описание класса SSL-ACCEPTOR.

*default-connection-timeout*

[Специальная переменная]

По-умолчанию, задержка соединения используется при чтении и записью acceptor'ом в поток сокета. Помните, что некоторые реализации Lisp позволяют Вам устанавливать различные задержки для чтения и записи, таким образом, Вы можете указать оба значения через инициализирующие аргументы при создании acceptor'а.

acceptor-remove-session acceptor session => generalized-boolean

[Общая функция]

Эта функция вызывается каждый раз, когда происходит разрушение ACCEPTOR сессии, поскольку возникает задержка сессии или явный вызов REMOVE-SESSION.

Модификация поведения acceptor'а

Если Вы хотите модифицировать действия acceptor'ов, Вы должны создать подкласс ACCEPTOR (или SSL-ACCEPTOR) и модифицировать основные функции управляющие поведением acceptor'а. Жизнь acceptor'а выглядит примерно так: Acceptor запускается функцией START, которая незамедлительно вызывает START-LISTENING и затем применяет функцию EXECUTE-ACCEPTOR для менеджера. Эта функция в конечном итоге вызывает ACCEPT-CONNECTIONS, которая ответственна за ожидание соединения со стороны клиентов. Для каждого входящего соединения вызывается функция HANDLE-INCOMING-CONNECTION, обращающейся к хозяину задачи, который в конечном итоге прямо вызывает PROCESS-CONNECTION или создаёт нить для последующего вызова PROCESS-CONNECTION. PROCESS-CONNECTION прежде всего вызывает INITIALIZE-CONNECTION-STREAM, затем выбирает и вызывает функцию управляющую запросом, посылает ответ клиенту и в конце вызывает RESET-CONNECTION-STREAM. Если соединение является не разрываевым, эта процедура также повторяется снова (за исключением шагов инициализации) до тех пор, пока соединение не закроется. Acceptor останавливается через функцию STOP.
Если Вы хотите использовать только стандартные acceptor'ы, поставляемые с Hunchentoot, то в этом случае, Вам ничего не надо знать о функциях перечисленных в этой секции.

start-listening acceptor => |

[Общая функция]

Устанавливает прослушиваемый сокет для заданного acceptor'а и запускает приём входящих соединений. Эта функция вызывается из нити, изначально запускающий acceptor и может вернуть ошибку с прослушиваемой операции (похожей на 'address in use').

accept-connections acceptor => nil

[Общая функция]

Работает в цикле, принимает соединения и управляет ими через acceptor менеджера для обеспечения использования HANDLE-INCOMING-CONNECTION. На LispWorks, эта функция немедленно возвращается всякий раз, на других реализациях Lisp она возвращается при остановке acceptor'а.

process-connection acceptor socket => nil

[Общая функция]

Эта функция вызывается менеджером при создании соединения с новым клиентом. Его аргументы - объект ACCEPTOR и управление сокетом LispWorks или потоковый объект usocket. Эта функция считывает заголовок запроса, настраивает объекты запроса и ответа и управляет ими через PROCESS-REQUEST, которая в свою очередь вызывает HANDLE-REQUEST для выборки и вызова управляющей функции запроса и ответа клиенту. Всё это выполняется в цикле, пока поток не закроется или пока не будет превышено время задержки соединения. Возможно это не лучшая идея для реализации, поэтому будьте осторожны при использовании этой функции.

initialize-connection-stream acceptor stream => stream

[Общая функция]

Может использоваться для модификации потока, выполняющего соединение между клиентом и сервером до чтения запроса. По умолчанию, метод ACCEPTOR ничего не выполняет, но посмотрите пример метода описанного для SSL-ACCEPTOR. Все методы этой общей функции должны возвращать поток для дальнейшего использования.

reset-connection-stream acceptor stream => stream

[Общая функция]

Сбрасывает поток, используемый для соединения между клиентом и сервером после каждого запроса, таким образом, становится возможным выполнить следующий запрос. Эта общая функция вызывается после запроса и должна возвращать поток.

acceptor-log-access acceptor &key return-code content content-length

[Общая функция]

Функция для ведения протоколирования доступа к acceptor'у. Аргументы ключевых слов: возвращаемый код, содержимое и длина содержимого содержат дополнительную информацию о протоколировании. В дополнение, возможно использование стандартных функций доступа к запросу для получения более подробной информации.

acceptor-log-message acceptor log-level format-string &rest format-arguments

[Общая функция]

Функция для вызова сообщений протоколирования от acceptor'а. Она должна принимать несколько уровней сообщений, которые должны входить в один из следующих категорий: :ERROR, :INFO или :WARNING, форматирование строки и произвольное число форматирующих аргументов.

acceptor-status-message acceptor http-return-code content

[Общая функция]

Эта функция вызывается после менеджера запроса, и используется для преобразования HTTP-STATUS-CODE в HTML сообщение, отображаемое пользователю. Если эта функция возвращает строку, эта строка будет передана клиенту взамен содержимого выданного менеджером. Если ERROR-TEMPLATE-DIRECTORY установлено в текущий acceptor и директория содержит файлы соответствующие HTTP-STATUS-CODE с названиями <cоde>.html, то эти файлы будут переданы клиенту после замены переменных. Переменные описаны в ${<variable-name>}. Дополнительные ключевые аргументы могут использоваться для замены переменных. Эти переменные можно интерполировать в сообщения об ошибках, содержащие текущий URL относящийся к серверу и без GET параметров. Также доступны ключевые переменные script-name, lisp-implementation-type, lisp-implementation-version и hunchentoot-version.

Менеджеры (Taskmasters)

Как "обычный" пользователь Hunchentoot, Вы можете смело игнорировать менеджеры задач и пропустить эту секцию. Но, если Вы продолжаете это читать, есть кое-какие детали: Каждый acceptor имеет менеджера, ассоциированного с его временем создания. Это работа менеджера: распределять работу по принятию и управлению входящими соединениями. Acceptor вызывает менеджера в случае необходимости, далее менеджер возвращает управление acceptor'у. Это действие происходит с помощью использования общих функций описанных в этих и предыдущих секциях. Hunchentoot поставляется с двумя стандартными реализациями менеджеров - один (используемый по умолчанию в мульти потоковых реализациях Lisp), запускает новую нить для каждого входящего соединения и другой, управляющий всеми запросами по очереди.

Вы можете контролировать ресурсы потребляемые менеджером с помощью двух инициализирующих аргументов. :max-thread-count позволяет установить максимальное число нитей запроса, которые могут работать одновременно. Если установлено в nil, то количество нитей не ограничивается. :max-accept-count позволяет установить максимальное число запросов, которые могут быть приняты (то есть будут обработаны или будут поставлены в очередь на обработку). Если :max-thread-count указан и :max-accept-count установлен в NIL, то будет возвращена ошибка +HTTP-SERVICE-UNAVAILABLE+ в случае, если количество обрабатываемых запросов больше чем есть больше чем max-thread-count. Если соблюдены оба значения :max-thread-count и :max-accept-count, то max-thread-count должно быть меньше чем max-accept-count; если количество произведённых запросов больше чем max-thread-count, то запросы до значения max-accept-count будут поставлены в очередь, до тех пор, пока будут доступна нить. Если количество выданных запросов превысит max-accept-count, то будет выдана ошибка +HTTP-SERVICE-UNAVAILABLE+. В среде балансировки нагрузки с множественными Hunchentoot серверами, это является причиной указать :max-thread-count, но оставить в null :max-accept-count. В этом случае при возникновении +HTTP-SERVICE-UNAVAILABLE+, (при исчерпании ресурсов одного сервера) балансировщик нагрузки будет пытаться найти другой сервер. В среде с единственным Hunchentoot сервером, полезно указать :max-thread-count и какое нибудь большее значение для :max-accept-count. Если ресурсы сервера будут уменьшаться, то будет возникать задержка; если сервер окончательно исчерпает ресурсы, то будет возвращена +HTTP-SERVICE-UNAVAILABLE+. По умолчанию, для этих переменных предпочтительны значения 100 и 120.

Если Вы хотите реализовать свой собственный менеджер задач, Вы должны создать подкласс TASKMASTER или один из его подклассов: SINGLE-THREADED-TASKMASTER или ONE-THREAD-PER-CONNECTION-TASKMASTER и определить общие функции для этих секций.

taskmaster

[Стандартный класс]

Экземпляр этого класса отвечает за распределение работы по обработке запросов на их acceptor'ы. Это "абстрактный" класс, цель использования которого - создавать экземпляры подклассов МЕНЕДЖЕРА.

one-thread-per-connection-taskmaster

[Стандартный класс]

Менеджер, который запускает одну нить для прослушивания входящих запросов и по нити на каждое входящее соединение.
Это реализация менеджера по умолчанию для много поточных реализаций языка Lisp.

single-threaded-taskmaster

[Стандартный класс]

Менеджер, запускаемый синхронно в нити, вызываемой функцией START (или в случае LispWorks, в нити запущенной с помощью COMM:START-UP-SERVER). Это самая простейшая реализация менеджера задач, не выполняющий ничего кроме вызова "родственных" методов acceptor'а - EXECUTE-ACCEPTOR вызывает ACCEPT-CONNECTIONS, HANDLE-INCOMING-CONNECTION вызывает PROCESS-CONNECTION.

execute-acceptor taskmaster => result

[Общая функция]

Это обратный вызов от acceptor'а, будучи однажды выполненным, эта функция запускает все процессы для прослушивания входящих соединения (смотрите START-LISTENING). Обычно вызывается метод ACCEPT-CONNECTIONS acceptor'а, но зависимость от экземпляра менеджера задач приводит к тому, что метод может быть вызван с новой нити.

handle-incoming-connection taskmaster socket => result

[Общая функция]

Эта функция вызывается acceptor'ом для запуска обработки запроса нового входящего соединения. socket - это экземпляр usocket, который представляет новое соединение (или обработку сокета в LispWorks). Менеджер задач запускает обработку запросов на входящем соединении с помощью вызова метода PROCESS-CONNECTION экземпляра acceptor'а. Аргумент сокета передаётся PROCESS-CONNECTION в виде аргумента. Если менеджер задач является много задачным, HANDLE-INCOMING-THREAD будет вызывать CREATE-TASKMASTER-THREAD, который в свою очередь вызовет PROCESS-CONNECTION в новой нити. HANDLE-INCOMING-THREAD вернёт ошибку +HTTP-SERVICE-UNAVAILABLE+ если будет порождено слишком много нитей запроса или приведёт к блокированию нити запроса, пока не завершится работа с другими запросами.

create-taskmaster-thread taskmaster socket => thread

[Общая функция]

Эта функция вызывается с помощью HANDLE-INCOMING-THREAD для создания новой нити, которая вызовет PROCESS-CONNECTION. Если Вы измените эту функцию, то обязательно проследите за тем, чтобы нить вызывала DECREMENT-TASKMASTER-REQUEST-COUNT перед выходом. Обычный метод выглядит так:
    (defmethod create-taskmaster-thread ((taskmaster monitor-taskmaster) socket)
                (bt:make-thread
                 (lambda ()
                   (with-monitor-error-handlers
                     (unwind-protect
                          (with-monitor-variable-bindings
                            (process-connection (taskmaster-acceptor taskmaster) socket))
                       (decrement-taskmaster-request-count taskmaster))))))

shutdown taskmaster => taskmaster

[Общая функция]

Выключает менеджер задач, то есть освобождает все ресурсы, которые были заняты менеджером задач. Например: многозадачный менеджер задач завершит все нити, которые были ассоциированы с ним. Эта функция вызывается acceptor'ским STOP методом.

taskmaster-acceptor taskmaster => acceptor

(setf (taskmaster-acceptor taskmaster ) new-value)

[Общий accessor]

Это accessor для слота объекта МЕНЕДЖЕРА ЗАДАЧИ, который создаёт обратную связь с acceptor'ом, связанным с ним. This is an accessor for the slot of a TASKMASTER object that links back to the acceptor it is associated with.

Управление запросами и их обработка

Главная работа HANDLE-REQUEST - это выбор и вызов функций, которые управляют запросами, то есть, функции которые просматривают данные переданные клиентом и формируют соответствующий ответ для клиента. По умолчанию это реализовано так:

Класс ACCEPTOR определяет общую функцию ACCEPTOR-DISPATCH-REQUEST, которая используется для управления запросом. Эта функция, по умолчанию, вызывается методом HANDLE-REQUEST. Каждый метод ACCEPTOR-DISPATCH-REQUEST просматривает объект запроса и в зависимости от его содержимого решает управлять ли запросом дальше или вызывать следующий метод.

В процессе управления запросом, Hunchentoot вызывает общую функцию ACCEPTOR-DISPATCH-REQUEST. Метод для ACCEPTOR пытается обслужить статичный файл относящийся к его ACCEPTOR-DOCUMENT-ROOT. Приложение определяет подклассы acceptor, которые выполняют разбор URL и выполняют требуемые действия.

Метод по умолчанию HANDLE-REQUEST устанавливает стандартное протоколирование и управление ошибками до вызова acceptor'ского диспетчера запросов.

Управление запросами происходит, если необходимо, с помощью модификации объекта ответа и с помощью возвращения, по событиям, тела ответа в форму в виде строки или бинарной последовательности. В качестве альтернативы, они могут вызывать SEND-HEADERS и писать прямо в поток.

Использования каркаса easy-handler (простое управление)

Класс EASY-ACCEPTOR определяет метод для ACCEPTOR-DISPATCH-REQUEST, который проходит через список *DISPATCH-TABLE*, содержащий функции управления. Каждый из этих функций принимает объект запроса для управления запросом или NIL, означающий переход к следующему диспетчеру. Если все диспетчеры функций вернут NIL, будет вызван следующий ACCEPTOR-DISPATCH-REQUEST.

Все функции и переменные в этой секции связаны с механизмом простого управления запросом и будут бессмысленными если Вы используете собственный диспетчер.

easy-acceptor

[Стандартный класс]

Этот класс не описывает дополнительные слоты для ACCEPTOR'а. Этот класс играет роль дополнительного типа для диспетчерских вызовов ACCEPTOR-DISPATCH-REQUEST. Эти классы и их подклассы нужны для использования каркаса easy-handler.

easy-ssl-acceptor

[Стандартный класс]

Этот класс - это смесь классов SSL-ACCEPTOR и EASY-ACCEPTOR. Используется в случаях когда нужна поддержка ssl и каркас easy-handler.

*dispatch-table*

[Специальная переменная]

Глобальный список управляющих функций. Инициализирующее значение - список содержащий символ DISPATCH-EASY-HANDLERS.

create-prefix-dispatcher prefix handler => dispatch-fn

[Функция]

Удобная функция возвращающая диспетчер, возвращающий управление (handler) всякий раз, когда часть пути URI запроса начинается с префиксом (prefix).

create-regex-dispatcher regex handler => dispatch-fn

[Функция]

Удобная функция, возвращающая диспетчер, возвращающий управление всякий раз, когда часть пути URI запроса regex'у регулярного выражения CL-PPCE (которая должна быть строкой, s-выражением или сканером).

create-folder-dispatcher-and-handler uri-prefix base-path &optional content-type => dispatch-fn

[Функция]

Создаёт и возвращает управляющую диспетчерскую функцию, которая управляет управляющей функцией, создающую файл относительно базового пути, раскомментированного uri-префиксом URI запроса. uri-префикс должен быть строкой заканчивающейся слэшем, а основной путь должен быть указателем на существующую директорию. Используется HANDLE-STATIC-FILE.
Если content-type не равняется NIL, то он (content-type) будет использоваться в качестве типа содержимого для всех файлов в директории. В противном случае (по умолчанию) тип содержимого для каждого файла определяется как обычно.


create-static-file-dispatcher-and-handler uri path &optional content-type => result

[Функция]

Создаёт и возвращает диспетчерскую функцию для запроса, управляющего функцией управления создающую файл раскомментированный указателем пути PATH с типом содержимого CONTENT-TYPE если SCRIPT-NAME запроса соответствует строке URI. Если CONTENT-TYPE равен NIL, то производится попытка определить тип содержимого через файловый суффикс.

define-easy-handler description lambda-list [ [ declaration* | documentation ] ] form*

[Макрос]

Описывает управление подобно DEFUN и опционально регистры с URI, так что они могут быть найдены с помощью DISPATCH-EASY-HANDLERS.
описание (description) - это или символьное имя (name) или список соответствующий разрушаемому лямбда списку.
 (name &key uri acceptor-names default-parameter-type default-request-type).
lambda-list - это список элементов состоящих или из символа var или из списка соответствующего разрушаемому лямбда списку.
 (var &key real-name parameter-type init-form request-type).
В результате управление будет являться Lisp функцией с именем name и ключевыми параметрами названных в символах var. Каждый var связан со значением GET или POST параметров вызванных real-name (строка) перед телом исполняемой функции. Если real-name не указано, оно будет вычислено с помощью преобразования в нижний регистр символьного имени var.
Если есть uri (которое вычисляется), то оно должно быть строкой или указателем функции для унарной функции. В этом случае, обработчик будет возвращён с помощью DISPATCH-EASY-HANDLERS, если uri строка и имя сценария текущего запроса равно uri или если uri указывает на функцию и применяет эту функцию к текущему REQUEST объекту, то в возвращается значение true.
имена-acceptor'ов (acceptor-names) (которые вычисляются) могут быть списком символов, которые обозначают что обработчик будет возвращён с помощью DISPATCH-EASY-HANDLERS в acceptor'ы имеющие одно из этих имён (смотрите ACCEPTOR-NAME). имена-acceptor'ов (acceptor-names) также могут быть символом T, означающим что обработчик будет возвращён с помощью DISPATCH-EASY-HANDLERS в каждом acceptor'е.
Параметр GET или POST (или оба) будут выбираться в зависимости от типа-запроса (request-type). Типы запроса могут быть следующими: :GET, :POST, :BOTH или NIL. В последнем случае будет использовано значение типа-запроса-по-умолчанию (default-request-type) (умолчание которого равно :BOTH).
Значение var обычно является строкой (если не является результатом загрузки файла, в этом случае оно не будет преобразовано на всех), но если тип-параметра (parameter-type) (являющееся вычисляемым) указан, строка будет конвертирована в другой Lisp тип по следующим правилам:
Если соответствующие GET или POST параметры не указаны клиентом, значение var'а будет равно NIL. Если parameter-type равен 'STRING, значение var остаётся без изменений. Если parameter-type равен 'INTEGER и строка параметра содержит исключительно десятичные числа, значение var'а будет соответствовать целому, иначе будет NIL. Если parameter-type равен 'KEYWORD, значение var'а будет ключевым словом полученным в результате интернирования в пакет ключевого слова параметра строки в верхнем регистре. Если parameter-type равен 'CHARACTER и длина параметра строки равна единице, значение var'а равно единственному символу этой строки, в другом случае - NIL. Если parameter-type равен 'BOOLEAN, значение var'а всегда будет T (конечно за исключением того случая когда NIL идёт правилом выше). Если parameter-type является другим атомом, предполагается наличие указателя функции для унарных функций, вызываемых для конвертирования строки в что-нибудь ещё.
Эти правила относятся к простым типам параметра, но parameter-type может быть списком, начинающимся с символов LIST или ARRAY или HASH-TABLE. Второе значение списка всегда должно быть простым типом параметра как в последнем параграфе - ниже мы будем называть его внутренним типом.
В случае 'LIST'а, все GET/POST параметры названные как real-name будут собраны, сконвертированы во внутренний тип по правилам выше и собраны в список, являющийся значением var.
В случае 'ARRAY, всем GET/POST параметрам будут присвоены имена подобные результату следующего выражения
(format nil "~A[~A]" real-name n)
где n - это не-отрицательное целое число, собранное в массив, где n-ый элемент устанавливается соответственно после конверсии во внутренний тип. Массив, который выступает в роли значения var, должен быть достаточно большим для хранения всех соответствующих параметров, но не больше. Н описанные элементы массива будут равны NIL. Помните, что VAR всегда будет обозначать массив, который может быть пустым и никогда не будет NIL'ом, даже если не найдено подходящих GET/POST параметров.
Полная форма параметра 'HASH-TABLE будет такой
(hash-table inner-type key-type test-function)
но key-type и test-function могут быть пропущены, в этом случае будут применены значения 'STRING и 'EQUAL, соответственно. Для этих типов параметра, все GET/POST параметры имеющие имена будут такими
(format nil "~A{~A}" real-name key)
(где key - это строка, которая не содержит фигурных скобок) будут принимать значения (после преобразования во внутренний тип) хэш таблицы с тестовой функцией test-function, где ключ (после преобразования в key-type) будет соответствовать ключу. Помните, что var всегда будет находиться в хэш таблице, которая может быть пустой, и таким образом никогда не будет равен NIL, даже если не найдены соответствующие GET/POST параметры.
В более сложных вопросах, три объединённых типов параметра также имеют сокращённую форму - только один из символов LIST, ARRAY или HASH-TABLE. В этом случае, внутренний тип, по умолчанию, будет равен 'STRING.
Если parameter-type не предоставлен или NIL, то взамен будет использован default-parameter-type (по умолчанию, там где 'STRING).
Если результат вычисления свыше покажет что var связан с NIL, то взамен будет использоваться init-form и var будет связан с этим вычислением.
Обработчики будут составляться с макросом сконструированным так, что результирующая Lisp функция будет полезна и за пределами Hunchentoot. В частности, все вычисления параметров будут производиться с *REQUEST*, то есть если мы работаем с Hunchentoot запросом. В другом случае, var всегда будет связан с результатом вычисления init-form, если нет соответствующего ключевого аргумента.
Демонстрационный код поставляется вместе с Hunchentoot и содержит пример, который демонстрирует некоторые свойства DEFINE-EASY-HANDLER.

dispatch-easy-handlers request => result

[Функция]

Это диспетчер, который возвращает соответствующий обработчик определённый с помощью DEFINE-EASY-HANDLER, если он есть.

Объекты запроса

Для каждого входящего запроса, acceptorPROCESS-CONNECTION) создаёт REQUEST объект и делает его доступным для обработчиков через специальную переменную *REQUEST*. Этот объект содержит всю информацию о запросе и эта секция вобрала в себя функции, которые можно использовать для запроса в качестве объекта. Во всех функциях, где запрос опциональный или ключевой параметр, по умолчанию принимается *REQUEST*.

Если Вам нужно более тонкое управление поведением объектов запроса, Вы можете использовать подкласс REQUEST и инициализировать слот REQUEST-CLASS класса ACCEPTOR соответственно. Acceptor сгенерирует объект запроса класса, названного по этому слоту.

request

[Стандартный класс]

Объекты этого класса содержат всю информацию о входящем запросе. Они создаются автоматически acceptor'ами и доступны для соответствующего обработчика. Эти объекты нельзя связывать напрямую со слотами, но Вы можете использовать подкласс REQUEST для реализации нужного Вам поведения. Смотрите слот REQUEST-CLASS для класса ACCEPTOR.

*request*

[Специальная переменная]

Текущий REQUEST объект, в контексте запроса.

real-remote-addr &optional request => string{, list}

[Функция]

Возвращает входящий http 'X-Forwarded-For' обработчик как второе значение в форме списка IP адресов, а первый элемент этого списка - как первое значение, если этот обработчик существует. В другом случае - REMOTE-ADDR возвращается только как значение.

parameter name &optional request => string

[Функция]

Возвращает GET или POST параметр с именем name (строка) - или NIL если нет ничего. Если оба GET или POST параметра существуют с некоторым именем, то будет возвращён параметр GET. Поиск является чувствительным к регистру. Смотрите также GET-PARAMETER и POST-PARAMETER.

get-parameter name &optional request => string

[Функция]

Возвращает значение параметра GET (переданного через URI запрос) названного через name, в виде строки (или NIL если нет GET параметра с этим именем). Помните, что будет возвращено только первое значение, если клиент передал более чем один GET параметр с именем name. Также смотрите GET-PARAMETERS*.

post-parameter name &optional request => string

[Функция]

Возвращает значение параметра POST (переданного в теле запроса), названного строкой name. Помните, что будет возвращено только первое значение, если клиент передал более одного POST параметра с именем name. Это значение обычно является строкой (или NIL если нет параметра POST с таким именем). Если, однако, браузер отправит файл через форму multipart/form-data, значение этой функции будет списком из трёх элементов
    (path file-name content-type)
где path - это путь, указывающий место, где сохранён загруженный файл, file-name (строка) - имя файла, отправленное браузером и content-type (также строка) - это тип содержимого, отправленный браузером. Файл, указанный путём, будет удалён после обработки запроса - Вы можете перемещать или копировать файл куда либо ещё, если нужно сохранить его.
POST параметры будут вычислены если тип содержимого тела запроса был multipart/form-data или application/x-www-form-urlencoded. Хотя эта функция названа POST-PARAMETER, Вы можете проинструктировать Hunchentoot вычислять эти параметры для других запросов с помощью установки *METHODS-FOR-POST-PARAMETERS*.
Смотрите также POST-PARAMETERS и *TMP-DIRECTORY*.

get-parameters* &optional request => alist

[Функция]

Возвращает в виде alist все GET параметры (переданные через URI запрос). car каждого элемента этого списка является именем параметра, до тех пор, пока cdr будет его значением (в виде строки). Элементы этого списка идут в том порядке, в каком они переданы через URI запрос. Также смотрите GET-PARAMETER.

post-parameters* &optional request => alist

[Функция]

Возвращает alist всех POST параметров (как переданные через тело запроса). car каждого элемента этого списка является его именем, пока cdr является его значением. Элементы этого списка идут в том порядке, в каком они следуют в теле запроса.
Также смотрите POST-PARAMETER.

*methods-for-post-parameters*

[Специальная переменная]

Список запроса типов метода (в качестве ключевого слова) по которым Hunchentoot пытается вычислить post-параметры (post-parameters).

cookie-in name &optional request => cookie

[Функция]

Возвращает куки с именем name (строка) как отправленные браузером - или NIL если куки нет.

cookies-in* &optional request => alist

[Функция]

Возвращает alist всех куки ассоциированных с объектом запроса REQUEST.

host &optional request => host

[Функция]

Возвращает значение 'Host' для обработчика входящего http.

query-string* &optional request => string

[Функция]

Возвращает строку запроса принадлежащую объекту запроса REQUEST. Это часть строки, находящаяся за знаком вопроса (то есть, GET параметры).

referer &optional request => result

[Функция]

Возвращает http заголовок 'Referer' (sic!).

request-method* &optional request => keyword

[Функция]

Возвращает метод запроса как ключевое слово Lisp.

request-uri* &optional request => uri

[Функция]

Возвращает запрошенный URI.

server-protocol* &optional request => keyword

[Функция]

Возвращает протокол запроса как ключевое слово Lisp.

user-agent &optional request => result

[Функция]

Возвращает http заголовок 'User-Agent'.

header-in* name &optional request => header

[Функция]

Возвращает входящий заголовок с именем name. name может быть ключевым словом (рекомендуется) или строкой.

headers-in* &optional request => alist

[Функция]

Возвращает alist входящих заголовков, ассоциированных с объектом запроса REQUEST.

remote-addr* &optional request => address

[Функция]

Возвращает адреса порождённой формы текущего запроса.

script-name* &optional request => script-name

[Функция]

Возвращает имя файла объекта запроса REQUEST. Это запрошенный URI без строки запроса (то есть, GET параметры).

aux-request-value symbol &optional request => value, present-p

(setf (aux-request-value symbol &optional request ) new-value)

[Accessor]

Этот accessor может использоваться для ассоциирования произвольных данных с символом symbol в объекте запроса REQUEST. present-p имеет значение true, если были найдены такие данные, в другом случае он равняется NIL.

delete-aux-request-value symbol &optional request => |

[Функция]

Удаляет значение ассоциированное с символом из объекта запроса REQUEST.

authorization &optional request => result

[Функция]

Возвращает два значения: имя пользователя и пароль (если они есть) как закодированные в заголовке 'AUTHORIZATION'. Возвращает NIL если нет такого заголовка.

*hunchentoot-default-external-format*

[Специальная переменная]

Внешний формат используется для вычисления объекта REQUEST.

*file-upload-hook*

[Специальная переменная]

Если не NIL, то должна быть унарной функцией, которая вызывается с именем файла для каждого файла, загруженного в Hunchentoot. Имя файла обозначает временный файл в который записывается загруженный файл. Hook (ловушка) вызывается сразу после создания файла. В этой точке, *REQUEST* связывается с текущим объектом REQUEST, но, очевидно, Вы не сможете получить доступ к post параметрам.

raw-post-data &key request external-format force-text force-binary want-stream => raw-body-or-stream

[Функция]

Если есть тело запроса, то возвращает содержимое, отправленное клиентом (за исключением содержимого типа multipart/form-data, в этом случае будет возвращён NIL). По умолчанию, результат является строкой, если Content-Type носителя типа является "text" или вектором октетов, в другом случае. В случае строки, внешний формат используется для декодирования содержимого определённого из параметра кодировки, отправленной клиентом (или, в другом случае, будет использован *HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*).
Также Вы можете явно указать внешний формат (через external-format), в этом случае результат будет только строкой. Более того, Вы можете использовать значение true для force-text, которое укажет Hunchentoot выступать в качестве типа носителя "text" (с external-format, если это предусмотрено). Или Вы можете установить значение true для force-binary, что обозначает использование вектора октетов в данном случае. (Если оба значения для force-text и force-binary установлены в true, то будет выдан сигнал ошибки).
Однако, если Вы укажете значение true для want-stream, другие параметры будут игнорироваться и Вы сможете читать поток содержимого (flexi) от самого себя. Ваша обязанность - считывать правильное количество данных, иначе Вы не сможете возвращать отклик клиенту. В потоке, позиция октетов установлена в 0. Если клиент указал заголовок для Content-Length, то это значение также связывается с потоком, и не важно, использует ли клиент фрагментированную кодировку или нет, Вы всегда будете считывать поток до достижениея EOF.
Если тип содержимого запроса был multipart/form-data или application/x-www-form-urlencoded, содержимое считывается Hunchentoot'ом и Вы не сможете прочитать его с потока.
Вы можете вызвать RAW-POST-DATA много раз в течении запроса, но Вы не можете смешивать вызовы имеющие различные значения для want-stream.
Помните, что эта функция не совсем верно названа, потому что клиент может отправлять содержимое, если метод запроса не является POST.

recompute-request-parameters &key request external-format => |

[Функция]

Пересчитывает параметры GET и POST для объекта запроса REQUEST. Доступно только в том случае, если Вы переключили внешние форматы во время запроса.

process-request request => nil

[Общая функция]

Эта функция вызывается PROCESS-CONNECTION'ом после прочитанного входящего заголовка. Вызывает HANDLE-REQUEST (это всего лишь обёртка) для выбора и вызова обработчика, а также для отправки вывода от этого обработчика к клиенту. Помните, что этот PROCESS-CONNECTION вызывается раз в соединение и повторяется в случае постоянных соединений. PROCESS-REQUEST вызывается для каждого запроса.
Возвращаемое значение этой функции игнорируется.
Подобно PROCESS-CONNECTION, это другая функция поведения, которую Вы можете модифицировать только тогда, когда Вы твёрдо уверены в том, что делаете.

handle-request acceptor request => content

[Общая функция]

Эта функция вызывается PROCESS-REQUEST'ом раз в прочитанный запрос и при созданном объекте REQUEST. Её работа - это обработка запроса, то есть, возвращение чего-либо клиенту.
Метод по-умолчанию вызывает диспетчера acceptor запросов, но, конечно же, Вы можете реализовать другое поведение. Также, метод по-умолчанию устанавливает стандартную обработку ошибок для обработчика.
Эта функция - удобное место для привязки специальных переменных, которые могут быть доступны для Ваших обработчиков.

acceptor-dispatch-request acceptor request => content

[Общая функция]

Эта функция вызывается для управляемого запроса при стандартном ведении протокола и обработки ошибки. Подкласс ACCEPTOR реализует методы для этой функции в установленном порядке маршрутизации запроса. Если метод не нужен для обработки запроса, он используется для вызова CALL-NEXT-METHOD, таким образом для следующего ACCEPTOR'а в наследуемой цепочке есть шанс попасть под обработку запроса.

cookies-in request => cookies

get-parameters request => get-parameters

header-in name request => result

headers-in request => headers

post-parameters request => post-parameters

query-string request => query-string

remote-addr request => address

remote-port request => port

request-acceptor request => acceptor

request-method request => method

request-uri request => uri

server-protocol request => protocol

script-name request => result

Это различные общие считыватели, используемые для чтения информации об объекте REQUEST. Если Вы пишете обработчик, не используйте эти считыватели, а используйте соответствующие функции с астериском (*) в конце имён, они также перечислены в этой секции. Эти общие считыватели предназначены для пользователей, которые хотят создать свой собственный REQUEST подкласс.

Объекты ответа

Для каждого входящего запроса, acceptorPROCESS-CONNECTION) создаёт объект REPLY и делает его доступным для обработчиков через специальную переменную *REPLY*. Этот объект содержит всю соответствующую информацию (за исключением содержимого тела) об ответе, который будет отправлен клиенту и эта секция содержит функции, которые можно использовать в запросах и модифицировать как объект. Во всех функциях, где ответ является дополнительным свойством или ключевым параметром, по умолчанию устанавливается *REPLY*.

Если Вам нужен более тонкое управление поведением объектов ответа, Вы можете использовать подкласс REPLY и, соответственно, инициализировать слот REPLY-CLASS класса ACCEPTOR. Acceptor будет генерировать объекты ответа для класса, названного по этому слоту.

reply

[Стандартный класс]

Объекты этого класса содержат всю информацию об исходящем ответе. Они создаются автоматически Hunchentoot'ом и доступны с помощью модификации соответствующего обработчика.
Не смешивайте напрямую эти объекты со слотами, но Вы можете использовать подкласс REPLY в реализации своего собственного поведения. Смотрите инициализирующий аргумент класса ACCEPTOR - :reply-class.

*reply*

[Специальная переменная]

Текущий REPLY объект в контексте запроса.

header-out name &optional reply => string

(setf (header-out name &optional reply ) new-value)

[Accessor]

HEADER-OUT возвращает исходящий http заголовок, названный по имени ключевого слова, если же ключевое слово отсутствует, то устанавливается в NIL. SETF для HEADER-OUT меняет текущее значение заголовка, с именем name. Если заголовок с именем name не существует, то он будет создан. Для обратной совместимости, name также может быть строкой, в этом случае ассоциация между заголовком и его именем чувствительна к регистру.
Помните, что заголовок 'Set-Cookie' не может быть запрошен с помощью HEADER-OUT и не должен устанавливаться с помощью SETF для HEADER-OUT. Смотрите также HEADERS-OUT*, CONTENT-TYPE*, CONTENT-LENGTH*, COOKIES-OUT* и COOKIE-OUT.

headers-out* &optional reply => alist

[Функция]

Возвращает alist исходящих заголовков, связанных с объектом ответа REPLY. Смотрите также HEADER-OUT.

content-length* &optional reply => content-length

(setf (content-length* &optional reply ) new-value)

[Accessor]

Исходящий 'Content-Length' http заголовок ответа.

content-type* &optional reply => content-type

(setf (content-type* &optional reply ) new-value)

[Accessor]

Исходящий 'Content-Type' http заголовок ответа.

cookie-out name &optional reply => result

[Функция]

Возвращает текущее значение исходящего куки с именем name. Поиск чувствителен к регистру.

cookies-out* &optional reply => alist

(setf (cookies-out* &optional reply ) new-value)

[Accessor]

Возвращает или устанавливает alist исходящих куки, ассоциированных с объектом ответа REPLY.

return-code* &optional reply => return-code

(setf (return-code* &optional reply ) new-value) [Accessor]

Получает или устанавливает http код возврата для ответа. Предварительно, код возврата каждого REPLY объекта устанавливается в +HTTP-OK+.

send-headers => stream

[Функция]

Отправляет инициализирующий статус строки и все заголовки как определённый REPLY объектом *REPLY*. Возвращает двоичный поток, в который можно записать тело ответа. После вызова этой функции, дальнейшие изменения *REPLY* не будут иметь эффекта. Также, для этого запроса будет отключена автоматическая обработка ошибок (то есть, отправка соответствующего кода статуса браузеру и т.д.), кроме того, не будут работать функции подобные REDIRECT или ABORT-REQUEST-HANDLER.
Если Ваш обработчик возвращает полное тело в виде строки или в виде массива октетов, то не следует вызывать эту функцию. Если обработчик вызывает SEND-HEADERS, то возвращаемое значение будет игнорироваться.

reply-external-format* &optional reply => external-format

(setf (reply-external-format* &optional reply ) new-value)

[Accessor]

Получает или устанавливает внешний формат ответа, используемый для вывода символов.

*default-content-type*

[Специальная переменная]

Устанавливает content-type по-умолчанию для заголовка, возвращаемого клиенту.

+http-continue+

+http-switching-protocols+

+http-ok+

+http-created+

+http-accepted+

+http-non-authoritative-information+

+http-no-content+

+http-reset-content+

+http-partial-content+

+http-multi-status+

+http-multiple-choices+

+http-moved-permanently+

+http-moved-temporarily+

+http-see-other+

+http-not-modified+

+http-use-proxy+

+http-temporary-redirect+

+http-bad-request+

+http-authorization-required+

+http-payment-required+

+http-forbidden+

+http-not-found+

+http-method-not-allowed+

+http-not-acceptable+

+http-proxy-authentication-required+

+http-request-time-out+

+http-conflict+

+http-gone+

+http-length-required+

+http-precondition-failed+

+http-request-entity-too-large+

+http-request-uri-too-large+

+http-unsupported-media-type+

+http-requested-range-not-satisfiable+

+http-expectation-failed+

+http-failed-dependency+

+http-internal-server-error+

+http-not-implemented+

+http-bad-gateway+

+http-service-unavailable+

+http-gateway-time-out+

+http-version-not-supported+

[Константы]

Значения этих констант следующие: 100, 101, 200, 201, 202, 203, 204, 205, 206, 207, 300, 301, 302, 303, 304, 305, 307, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 424, 500, 501, 502, 503, 504 и 505. Смотрите RETURN-CODE.

content-length reply => content-length

content-type reply => content-type

headers-out reply => headers-out

[Общие считыватели]

Это различные общие считыватели, которые используются для чтения информации об объекте REPLY. Если Вы пишете обработчик, не используйте эти считыватели, а используйте соответствующие функции с астериском (*) в конце имён, они перечислены в этой секции. Эти общие считыватели всего лишь экспортированы для пользователей, которые хотят создать свой собственный подкласс REPLY.

cookies-out reply => result

(setf (cookies-out reply ) new-value)

return-code reply => result

(setf (return-code reply ) new-value)

reply-external-format reply => result

(setf (reply-external-format reply ) new-value)

[Общие accessor'ы]

Эти различные общие accessor'ы, которые используются для запроса и модификации REPLY объектов. Если Вы пишете обработчик, не используйте эти accessor'ы, взамен следует использовать соответствующие функции с астериском (*) в конце имени, эти функции перечислены в этой секции. Эти общие accessor'ы предназначены для тех пользователей, которым нужно создать свой собственный подкласс REPLY.

Сессии

Hunchentoot поддерживает сессии: Как только обработчик запроса вызовет START-SESSION, Hunchentoot использует кукисы или (если клиент не возвращает куки) переписывает URL'ы для отслеживания клиента, то есть, для обеспечения 'определённости' для http протокола, который не поддерживает метод определения состояния клиента. Сессия ассоциируется с клиентом как CLOS объект, который можно использовать для сохранения независимых данных между запросами.

Hunchentoot предпринимает некоторые разумные приёмы для предотвращения подслушиваний со стороны хакерских (hijacking) сессий (смотрите ниже), но эти методы нельзя считать реальной защитой. Не храните приватные данные в сессиях и не полагайтесь на механизм сессий, как защиту от злоумышленников, которым нужны эти данные!

Для каждого запроса существует один SESSION объект, доступный обработчику через специальную переменную *SESSION*. Этот объект содержит всю доступную информацию о сессии, доступ к этому объекту можно получить через функцию, описанную в этой главе. Помните, что внутренняя структура SESSION объектов должна быть непрозрачной и может изменяться в будущих релизах Hunchentoot.

Сессии автоматически проверяются на действительность и возраст создания REQUEST объекта, то есть, если *SESSION* не NIL, то эта сессия считается действительной и не слишком старой. Старые сессии автоматически удаляются.


session

[Стандартный класс]

Объект SESSION поддерживается Hunchentoot'ом. MAKE-INSTANCE не создаёт явно этот объект, но неявно создаётся с помощью START-SESSION и отправляется в нить как непрозрачный объект.
Вы можете игнорировать Hunchentoot'овские SESSION объекты и реализовать Вашу собственную сессию, если Вы предоставляете соответствующие методы для SESSION-COOKIE-VALUE и SESSION-VERIFY.

start-session => session

[Функция]

Возвращает текущий SESSION объект. Если нет текущей сессии, то она будет создана и обновляет соответствующие структуры данных. В этом случае функция будет отсылать куки сессии браузеру.

session-value symbol &optional session => value, present-p

(setf (session-value symbol &optional session ) new-value) [Accessor]

Этот accessor может использоваться для ассоциирования независимых данных с символом symbol в объекте сессии SESSION. present-p равно true если есть такие данные, в другом случае NIL. По умолчанию, значение для сессии - *SESSION*.
Если вызвана функция SETF с NIL для SESSION-VALUE, то будет порождён экземпляр сессии с START-SESSION.

delete-session-value symbol &optional session => |

[Функция]

Удаляет значение ассоциированное с symbol и сессией, если она существует.

*session*

[Специальная переменная]

Текущая сессия в контексте запроса или NIL.

remove-session session => |

[Функция]

Окончательное удаление объекта сессии SESSION с внутренней базы данных сессий Hunchentoot.

reset-sessions &optional acceptor => |

[Функция]

Удаляет все сохранённые сессии acceptor'а. По умолчанию, acceptor'ом является *ACCEPTOR*.

*rewrite-for-session-urls*

[Специальная переменная]

Возможна ли перезапись HTML страниц для безкукисовой сессии.

*content-types-for-url-rewrite*

[Специальная переменная]

Типы содержимого для url-rewriting устанавливается в OK. Смотрите *REWRITE-FOR-SESSION-URLS*.

*use-remote-addr-for-sessions*

[Специальная переменная]

Указывает на необходимость перекодирования клиентского удалённого IP (возвращённого с помощью REAL-REMOTE-ADDR) в строку сессии. Если это значение установлено в true, то сессия будет недоступна в случае изменения IP удалённого клиента.
В качестве примера можно привести случай использования клиентом прокси сервера, который отправляет некорректные 'X_FORWARDED_FOR' заголовки.

session-remote-addr session => remote-addr

[Общая функция]

Удалённый IP адрес клиента, при старте этой сессии (возвращается через REAL-REMOTE-ADDR).

*use-user-agent-for-sessions*

[Специальная переменная]

Должен ли заголовок 'User-Agent' декодироваться в строку сессии. Если это значение установлено в true, сессия не будет доступна если клиент отправит другой 'User-Agent' заголовок.

session-user-agent session => user-agent

[Общая функция]

Входящий 'User-Agent' заголовок, присылаемый при создании сессии.

session-max-time session => max-time

(setf (session-max-time session ) new-value)

[Общий accessor]

Получает или устанавливает время простоя сессии (в секундах), по истечении которого сессия считается истёкшей.

*session-max-time*

[Специальная переменная]

Время, по умолчнаию, (в секундах) по истечении которого, сессия считается истёкшей.

*session-gc-frequency*

[Специальная переменная]

Сессионное GC (смотрите функцию SESSION-GC) происходящее каждый *SESSION-GC-FREQUENCY* запросы (считаются только запросы, создающие новую сессию), если только эта переменная не NIL. Смотрите SESSION-CREATED.

session-gc => |

[Функция]

Удаляет устаревшие сессии с базы данных текущих сессий - смотрите SESSION-TOO-OLD-P.

session-too-old-p session => generalized-boolean

[Функция]

Возвращает true в случае если объект сессии SESSION был не активен в последние (session-max-time session) секунды.

session-id session => session-id

[Общая функция]

Уникальное ID (целочисленное) сессии.

session-start session => universal-time

[Общая функция]

Время, когда была запущена сессия.

Изменение поведения сессий

При ежедневном использовании сессий Вы, скорее всего, используете только START-SESSION, SESSION-VALUE и, может быть, DELETE-SESSION-VALUE и *SESSION*. Однако, есть два пути изменения поддержки сессий Hunchentoot'ом.

Первый путь - в целом, оставить механизм сессий без изменений, и изменить только некоторые настройки:

  • Кодирование видимой части сессии, с помощью пароля, устанавливаемого Вами.
  • И сохранить его с использованием имени куки (или GET параметра), которое Вы можете перезаписать.
  • Каждая сессия получает новое ID, во время создания и Вы можете реализовать более надёжный путь для этого действия.
  • Вы можете настроить исполнение какого-либо триггера при вызове сессии. Это действие выполняется при реализации собственного сборщика мусора.
  • По умолчанию, все сессии сохраняются в глобальный alist в памяти. Вы не можете изменять alist'овскую часть, но Вы можете предоставлять Ваши сессии через различные "Базы Данных".
  • По умолчанию, каждая операция, которая модифицирует сессии или базу данных сессии блокируется глобальной блокировкой, но Вы можете настраивать эти блокировки.

Другой путь изменения Hunchentoot сессий - это полная их замена. Это делается довольно просто: Создаёте свой собственный класс для хранения состояния (не наследуемый от SESSION) и реализуете правильные методы для SESSION-VERIFY и SESSION-COOKIE-VALUE. Hunchentoot будет продолжать использовать куки и/или переписывать URL'ы для сохранения состояния сессии и сохранения "текущей сессии" (в зависимости от Вашей реализации) в *SESSION*. Поддержка всего остального (сохраняющиеся сессии, GC, получение и передача значений настроек) будет Вашей заботой, кроме того, не будут исполняться функции по работе с сессиями (подобные START-SESSION или SESSION-VALUE). (Практически) полная свобода, но, приветствуется наличие некоторой ответственности... :)


*session-secret*

[Специальная переменная]

Случайная ASCII строка, используемая для кодирования данных публичной сессии. Эта переменная изначально несвязанная и при необходимости, устанавливается (через RESET-SESSION-SECRET) в начале создания сессии. Вы можете избежать это путём установки своего значения перед запуском acceptor'ов.

reset-session-secret => secret

[Функция]

Устанавливает *SESSION-SECRET* в новое случайное значение. Все старые сессии будут считаться не актуальными.

session-cookie-name acceptor => name

[Общая функция]

Возвращает имя (строка) куки (или GET параметр), который используется для хранения сессии на клиентской стороне. По умолчанию, используется строка "hunchentoot-session", но Вы можете переписать эту функцию, если хотите использовать другое имя.

session-created acceptor new-session => result

[Общая функция]

Эта функция вызывается всякий раз, когда создаётся новая сессия. Этот метод, по умолчанию, можно использовать как триггер GC сессии основанный на значении *SESSION-GC-FREQUENCY*.
Возвращаемое значение игнорируется.

next-session-id acceptor => id

[Общая функция]

Возвращает следующий, по последовательности, ID сессии, который уникален для каждой сессии. Метод по умолчанию использует простой глобальный счётчик, который не блокируется. Для высоко-производительных систем Вы должны использовать более надёжную реализацию.

session-db acceptor => database

(setf (session-db acceptor ) new-value)

[Общий accessor]

Возвращает базу данных текущей сессии, которая представляет собой alist, где каждый car - это ID сессии и cdr - это соответствующий SESSION объект. По умолчанию используется глобальный список для всех acceptor'ов.

session-db-lock acceptor &key whole-db-p => lock

[Общая функция]

Функция, которая возвращает блокировку, используемую для предотвращения конкурентного доступа к сессиям. Первый аргумент должен быть acceptor'ом, который обрабатывает текущий запрос, второй аргумент устанавливается в true если модифицируется вся текущая база данных сессий. Если он установлен в NIL, то будет модифицироваться только одна существующая сессия в базе данных.
Эта функция может возвращать NIL, который обозначает что база данных сессий или сессии будет модифицироваться без состояния блокировки (на пример, для одно нитевой среды). По умолчанию, для реализаций Lisp'а, поддерживающих нити всегда возвращается глобальная блокировка (игнорируется аргумент acceptor'а), а в противном случае возвращается NIL.

session-verify request => session-or-nil

[Общая функция]

Пытается получить идентификатор сессии из куки (или, альтернативно, из GET параметра) отправляемого клиентом (смотрите SESSION-COOKIE-NAME и SESSION-COOKIE-VALUE). Это идентификатор проверяется на действительность с помощью запроса REQUEST объекта. При благополучном исходе возвращается соответствующий объект сессии (если он не слишком старый). Иначе возвращается NIL.
Метод предоставляется по умолчанию, также Вы можете написать свой собственный метод для поддержки своих собственных сессий.

session-cookie-value session => string

[Общая фукнция]

Возвращает строку, которая может быть использована для безопасного восстановления сессии session если сессия только что установлена. Используется как значение сохраняемое в куки сессии или в соответствующий GET параметр и проверяется через SESSION-VERIFY.
Метод предоставляется по умолчанию и нет причин его переписывать, если только Вы не хотите использовать его в своих объектах сессий.

Куки

Исходящие куки содержатся в объекте REPLY запроса (смотрите COOKIE-OUT и COOKIES-OUT*). Это CLOS объект описываемый следующим образом:

(defclass cookie ()
  ((name :initarg :name
         :reader cookie-name
         :type string
         :documentation "The name of the cookie - a string.")
   (value :initarg :value
          :accessor cookie-value
          :initform ""
          :documentation "The value of the cookie. Will be URL-encoded when sent to the browser.")
   (expires :initarg :expires
            :initform nil
            :accessor cookie-expires
            :documentation "The time (a universal time) when the cookie expires (or NIL).")
   (path :initarg :path
         :initform nil
         :accessor cookie-path
         :documentation "The path this cookie is valid for (or NIL).")
   (domain :initarg :domain
           :initform nil
           :accessor cookie-domain
           :documentation "The domain this cookie is valid for (or NIL).")
   (secure :initarg :secure
           :initform nil
           :accessor cookie-secure
           :documentation "A generalized boolean denoting whether this is a secure cookie.")
   (http-only :initarg :http-only
              :initform nil
              :accessor cookie-http-only
              :documentation "A generalized boolean denoting whether this is a HttpOnly cookie.")))

Считыватель COOKIE-NAME и accessor'ы COOKIE-VALUE, COOKIE-EXPIRES, COOKIE-PATH, COOKIE-DOMAIN, COOKIE-SECURE и COOKIE-HTTP-ONLY были экспортированы из пакета Hunchentoot. На данный момент, имя класса само по себе не экспортируется.


set-cookie name &key value expires path domain secure http-only reply => cookie

[Функция]

Создаёт COOKIE объект из параметров переданных этой функции и добавляет его в исходящее куки объекта ответа REPLY. Если куки с некоторым именем (чувствительно к регистру) уже существует, то оно будет заменено. По умолчанию, для ответа - это *REPLY*. По умолчанию, значение - это пустая строка.

set-cookie* cookie &optional reply => cookie

[Функция]

Добавляет объект куки COOKIE в исходящие куки объекта ответа REPLY. Если куки с некоторым именем (чувствительно к регистру) уже существует, оно будет заменено. По умолчанию, ответом является *REPLY*.

Протоколирование

Hunchentoot может вести протоколирование доступа и диагностических сообщений двумя различными путями: в какой-нибудь файл в файловой системе или потоки. Протоколирования можно отключить с помощью установки слотов ACCESS-LOG-DESTINATION и MESSAGE-LOG-DESTINATION в ACCEPTOR'е в NIL. Два слота могут быть инициализированы через инициализационные аргументы :ACCESS-LOG-DESTINATION и :MESSAGE-LOG-DESTINATION при создании acceptor'а или с помощью настройки слотов через accessor'ы ACCEPTOR-MESSAGE-LOG-DESTINATION и ACCEPTOR-ACCESS-LOG-DESTINATION.

Если путь для этих сообщений или принимаемых логов установлен в переменную, указывающую на поток вывода, то Hunchentoot пишет сообщения логов в этот поток. По умолчанию, Hunchentoot осуществляет вывод в *STANDARD-ERROR*.

Протоколирование доступа имеет формат похожий на формат логов web сервера Apache, таким образом анализаторы логов для Apache подходят и для Hunchentoot. Ошибки, происходящие во время обработки запросов записываются в отдельный файл.

Стандартный механизм ведения протоколов специально сделан простым и медленным. Лог файлы открываются для каждого сообщения лога и закрываются после осуществления записи, доступ к файлу закрывается с помощью глобальной блокировки. Предоставляемые классы acceptor'ов могут реализовать различные методы доступа и ведения логов для общих функций ACCEPTOR-LOG-MESSAGE и ACCEPTOR-LOG-ACCESS (например, для работы с центральным сервером логов или записи логов в отдельные файлы).

Ошибки, происходящие с обработчиком, отслеживаются Hunchentoot'ом с помощью протоколирования событий в ACCEPTOR-MESSAGE-LOG-DESTINATION.

log-message* log-level format-string &rest format-arguments => result

[Функция]

Удобная функция, вызывающая протоколирования сообщения для текущего acceptor'а (если он есть) с принятыми аргументами. Возвращает NIL при отсутствии логгера сообщений или при возникновении подобной ошибки.
Эту функцию использует Hunchentoot для ведения протоколов ошибок, получаемых при процессе обработки запроса.

*log-lisp-errors-p*

[Специальная переменная]

Должны ли протоколироваться ошибки Lisp в обработчике запросов.

*log-lisp-backtraces-p*

[Специальная переменная]

Протоколировать отладку Lisp. Работает только тогда, когда *LOG-LISP-ERRORS-P* установлено в true.

*log-lisp-warnings-p*

[Специальная переменная]

Протоколировать предупреждения генерируемые Lisp'ом в процессе обработки запроса.

*lisp-errors-log-level*

[Специальная переменная]

Уровень протоколирования для Lisp ошибок. Возможные значения: :ERROR (по умолчанию), :WARNING или :INFO.

*lisp-warnings-log-level*

[Специальная переменная]

Уровень протоколирования Lisp предупреждений. Возможные значения: :ERROR, :WARNING ( по умолчанию) или :INFO.

Обработка состояний и ошибок

Эта секция описывает как Hunchentoot работает с чрезвычайными ситуациями. Также, смотрите секцию о протоколировании.

В случае ошибки при обработке запроса, по умолчанию, Hunchentoot захватывает ошибку, заносит соответствующую запись в журнал логов и, опционально, отправляет ошибку в виде HTML запроса клиенту. Это поведение можно изменить через значения специальных переменных, задокументированных ниже.


*catch-errors-p*

[Специальная переменная]

Если значение этой переменной равно NIL (по умолчанию, установлено в T), то ошибки, происходящие при обработке запроса, не захватываются как обычно, а вызывается отладчик Lisp'а. Очевидно, что эта переменная должна устанавливаться в true при обычной работе. Для более тонкой настройки смотрите MAYBE-INVOKE-DEBUGGER.

*show-lisp-errors-p*

[Специальная переменная]

Отображать ли ошибки Lisp'а на HTML вывод. Помните, что данное свойство действует только для сообщений выдаваемых Lisp'ом. Если существует шаблон ошибки для кода статуса "internal server error" ("внутренняя ошибка сервера"), то эта специальная переменная не используется (смотрите acceptor-status-message).

*show-lisp-backtraces-p*

[Специальная переменная]

Выводить ли сообщения отладки на HTML вывод если *SHOW-LISP-ERRORS-P* установлен в true и есть ошибки в работе.

maybe-invoke-debugger condition => |

[Общая функция]

Эта общая функция вызывается каждый раз, когда приходит сигнал о состоянии condition в Hunchentoot. Для отладочных целей, Вы должны определить классы состояний. Если *CATCH-ERRORS-P* установлен в NIL, метод по умолчанию вызывает отладчик с состоянием condition.

hunchentoot-condition

[Тип состояния]

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

hunchentoot-error

[Тип состояния]

Суперкласс для всех ошибок, связанных с Hunchentoot'ом и подкласс HUNCHENTOOT-CONDITION.

parameter-error

[Тип состояния]

Сигнал с этим состоянием подаётся в случае вызова функции с противоречивыми или неправильными параметрами. Подкласс HUNCHENTOOT-ERROR.

hunchentoot-warning

[Тип состояния]

Суперкласс для всех предупреждающих сообщений связанных с Hunchentoot и подкласс HUNCHENTOOT-CONDITION.

Дополнительно

Различные функции и переменные, которые не вошли ни в одну из вышеперечисленных категорий.

abort-request-handler &optional result => result

[Функция]

Эта функция может быть вызвана в любое время обработчиком запроса для незамедлительного прерывания обработки запроса. Работает так, как будто обработчик вернул какой-либо результат result. Для примера смотрите исходный код REDIRECT.

handle-if-modified-since time &optional request => |

[Функция]

Эта функция предназначена для использования внутри обработчика. Если клиент отправил заголовок 'If-Modified-Since' (смотрите RFC 2616, раздел 14.25) и указанное время соответствует всемирному времени time то в этом случае клиенту будет сразу отправлен заголовок +HTTP-NOT-MODIFIED+ без какого-либо содержимого.
Помните, обычно, с помощью этой функции клиенту отправляют заголовки 'Last-Modified'. Для примера, смотрите исходный код CREATE-STATIC-FILE-DISPATCHER-AND-HANDLER.

handle-static-file path &optional content-type => nil

[Функция]

Отправляет файл обозначенный путём path с типом содержимого content-type клиенту. Устанавливает необходимые обработчики. Эта функция использует HANDLE-IF-MODIFIED-SINCE.
Если content-type равен NIL, функция пытается определить правильный тип содержимого из расширения файла, в случае, когда функция не смогла определить тип содержимого, используется "application/octet-stream".
Помните, что эта функция производит внутренний вызов SEND-HEADERS, так что после вызова этой функции непосредственно Вами будет произведена отправка заголовков с игнорированием значения возвращённого Вашим обработчиком.

redirect target &key host port protocol add-session-id code => |

[Функция]

Возвращает соответствующие заголовки для перенаправления клиента к target (строка).
Если target является полным URL начинающимся со схемы host, port, то protocol будет игнорироваться. В случае, когда target определяет часть пути URL, то protocol должен быть одним из следующих ключевых слов: :HTTP или :HTTPS и URL для перенаправления должен состоять из host, port, protocol и target.
Если code - это код перенаправления 3xx, то он будет отправлен как код статуса. В случае NIL, код статуса 302 будет отправлен клиенту. Если host не указан, будет использован текущий host (смотрите HOST). Если protocol - это ключевое слово :HTTPS, то клиент будет перенаправлен на https URL, если же protocol это ключевое слово :HTTP, то будет произведено перенаправление на http URL. Если и host и protocol не указаны, то значение протокола будет соответствовать текущему запросу.

require-authorization &optional realm => |

[Функция]

Возвращает соответствующие заголовки для требуемой базовой HTTP аутентификации (смотрите RFC 2617) для экземпляра realm. По умолчанию, значение для экземпляра "Hunchentoot".

no-cache => |

[Функция]

Добавляет соответствующие заголовки для окончательного предотвращения кэширования на большинстве браузерах.

ssl-p &optional acceptor => generalized-boolean

[Функция]

Установить безопасное соединение с клиентом. Смотрите ACCEPTOR-SSL-P.

reason-phrase return-code => string

[Функция]

Возвращает краткое описание для кода возврата HTTP return-code (должен быть целочисленным) или NIL для кодов возврата неизвестных Hunchentoot'у.

rfc-1123-date &optional time => string

[Функция]

Генерирует строку времени соответствующую RFC 1123. По умолчанию - это текущее время. Может использоваться для отправки заголовка 'Last-Modified' - смотрите HANDLE-IF-MODIFIED-SINCE.

url-encode string &optional external-format => string

[Функция]

Строка кодирования URL (URL-encodes) используется во внешнем формате external-format. По умолчанию, для external-format устанавливается значение *HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*.

url-decode string &optional external-format => string

[Функция]

Декодирование закодированной URL (URL-encoded) строки. Декодирование происходит с предполагаемым внешним форматом external-format, то есть, эта функция - противоположность URL-ENCODE. Предполагается, что Вы редко будете пользоваться этой функцией, если вообще будете пользоваться. Но на всякий случай эта функция есть. Значение для external-format, по умолчанию, устанавливается в *HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*.

escape-for-html string => result

[Функция]

Экранирующие символы #\<, #\>, #\', #\" и #\& для HTML вывода.

http-token-p object => generalized-boolean

[Функция]

Функция даёт ответ на вопрос: является ли объект object не пустой строкой, соответствующей RFC 2068 (то есть, можно ли его использовать, скажем, для имён куки).

mime-type pathspec => result

[Функция]

Учитывая обозначение пути pathspec возвращает MIME тип (в виде строки), соответствующий суффиксу файла, указанного через pathspec (или NIL).

within-request-p => generalized-boolean

[Функция]

Возвращает true, если в контексте запроса. Иначе, возвращает NIL.

*tmp-directory*

[Специальная переменная]

Должна представлять из себя путь, обозначающий директорию, где хранятся временные файлы. Используется для загрузки файлов на сервер.

*header-stream*

[Специальная переменная]

Если эта переменная не NIL, то она должна быть связана с потоком, в котором входящие и исходящие заголовки должны записываться для отладочных целей.

*cleanup-function*

[Специальная переменная]

Указатель для функции без аргументов, вызываемой на регулярной основе, в случае если *CLEANUP-INTERVAL* не NIL. Инициализационное значение - это имя функции, производящей сборку мусора на 32-битной версии LispWorks.
Эта переменная доступна только в LispWorks.

*cleanup-interval*

[Специальная переменная]

Должна равняться NIL или не отрицательному целому числу. Система вызывает *CLEANUP-FUNCTION* каждый *CLEANUP-INTERVAL* при создании новых работающих нитей (считаются глобально через все acceptor'ы), за исключением случая когда значение переменной не равно NIL. Инициализирующее значение равно 100.
Эта переменная доступна только в LispWorks.

Тестирование

Hunchentoot содержит тестовый сценарий, который проверяет что демонстрационный web сервер отвечает так, как надо. Этот тестовый сценарий использует клиентскую библиотеку Drakma HTTP и, таким образом, некоторая часть кода этой библиотеки содержится в Hunchentoot'е. Запуск этого тестового сценария является хорошим тестом Вашего web сервера, кроме того, возможен запуск сценария на другой машине для проверки нового Hunchentoor (или что важно, Drakma) порта.

Для запуска теста, запустите демонстрационный web сервер. Затем, в Вашем Lisp listener'е напечатайте

(hunchentoot-test:test-hunchentoot "http://localhost:4242")

Вы увидите некоторые диагностические сообщения и строку, в которой указано какие тесты не были успешно пройдены. (Также, Вы можете использовать пример сертификата, файлы ключа в директории теста и запустить тестирование https сервера.)

hunchentoot-test:test-hunchentoot base-url &key => |

[Функция]

Запуск встроенного теста. base-url - это URL используемый для тестирования, путь не должен содержать слэш разделения. Ключевые аргументы в данный момент не используются и предназначены для будущего применения.
Подразумевается, что демонстрационный тестируемый сервер запущен по заданному base-url. Сценарий выдаёт запросы на получение различных страниц с этого сервера и ожидает ответы на свои запросы.

Отладка

По умолчанию, Hunchentoot перехватывает все ошибки, происходящие в процессе работы обработчика запроса, протоколирует их в файл лога и отправляет статическую страницу ошибки пользователю. При разработке приложений может возникнуть ситуация, когда Вам понадобится изменить это стандартное поведение на вызов отладчика при возникновении ошибки. Для этого Вы можете установить значение *CATCH-ERRORS-P* в NIL. Или Вам может понадобится отображать детализированную информацию об ошибке в странице отображения ошибки. Для реализации такого поведения установите *SHOW-LISP-ERRORS-P* в true. Если Вы не хотите видеть отладочную информацию на странице ошибок, установите *SHOW-LISP-BACKTRACES-P* в NIL.

История

Предшественник Hunchentoot'а TBNL (сокращение от "To Be Named Later" - "Будет назван позже") вырастал в течении нескольких лет как набор инструментов, который Я использовал в различных коммерческих и частных проектах. В Августе 2003-го, Дэниель Барло (Daniel Barlow) начал пересмотр web API в списке рассылки lispweb и Я описал API своего не опубликованного кода (и окрещённого как "TBNL").

Оказалось, что Джефф Колдуэлл (Jeff Caldwell) уже работал над подобной вещью и он написал мне письмо с предложением объединить наши усилия. Поскольку Я не имел планов по публикации своего кода (который был плохо организован, не документирован и был в основном CMUCL-подобным), Я передал свой код Джеффу (Jeff) и он начал работать над подготовкой к релизу. Он написал документацию, добавил новые свойства, произвёл улучшение и переписал код на базе KMRCL для улучшения переносимости между некоторыми Lisp реализациями.

Но, к сожалению, у Джеффа (Jeff) тоже не оказалось достаточно времени чтобы довести работу до релиза. Но весной 2004-го года, моему клиенту понадобилась задокументированная версия кода, таким образом, пришла мысль что было бы неплохо опубликовать этот набор инструментов под открытой лицензией. Я взял код Джеффа, внёс поправки (для синхронизации с кодом, написанным в другое время) и расширил документацию. Результатом стал TBNL 0.1.0 (который первоначально требовал mod_lisp в качестве фронт-энда).

В марте 2005-го, Боб Хатчисон (Bob Hutchinson) прислал исправления, которые позволяли TBNL'у работать другими фронтэндами кроме mod_lisp. После этих событий, Я понял что TBNL стал почти полнофункциональным web сервером, со временем Я написал Hunchentoot, который стал полнофункциональным web сервером, реализованным как обёртка вокруг TBNL. Hunchentoot 0.1.0 был выпущен в конце 2005-го года и изначально предназначался только для LispWorks.

Hunchentoot 0.4.0 был выпущен в Октябре 2006-го и стал первым выпуском, работающим с остальными реализациями Common Lisp. Это была мажорная версия включившая в себя TBNL и полностью его заместившая.

Hunchentoot 1.0.0 был выпущен в Феврале 2009-го и снова представляла собой мажорную версию. Была введено использование usocket и библиотеки Bordeaux нитей для не-LispWorks реализаций Lisp'а, также был вычищен платформо-зависимый код. Контроль поведения нитей стал реализовываться через менеджеров (taskmasters). mod_lisp стал поддерживать те вещи, которые были удалены из этого релиза для упрощения кодовой базы (и отчасти из-за отсутствия интереса). Некоторые архитектурные изменения (не поддерживающие обратную совместимость) были сделаны с целью упрощения работы с поведением Hunchentoot'а. Значительная часть работы редизайна версии 1.0.0 была проделана Гансом Гюбнером (Hans Hübner).

Благодарности

Спасибо Джеффу Колдуэллу (Jeff Caldwell) - TBNL никогда бы не вышел без его усилий. Спасибо Стефану Шоллу (Stefan Scholl) и Трэвису Кроссу (Travis Cross) за различные дополнения и исправления к TBNL, Майклу Веберу (Michael Weber) за код загрузки файла и Дженису Дзеринсу (Janis Dzerins) за его RFC 2388 код. Спасибо Бобу Хатчисону (Bob Hutchison) за его код поддержки различных фронт-эндов (который заставил меня понять, что TBNL стал "почти" настоящим web сервером) и написал пример для UTF-8. Спасибо Гансу Гюбнеру (Hans Hübner) за архитектурные улучшения для релиза версии 1.0.0 и перевод документации в XHTML. Спасибо Джону Фодераро (John Foderaro) за послуживший вдохновением AllegroServe. Спасибо Уве ван Лоху (Uwe von Loh) за логотип Hunchentoot'а.

Вначале Hunchentoot использовал код из ACL-COMPAT от Йохана Шмидта (Jochen Schmidt). (В дальнейшем этот код был замещён кодом Chunga.) Когда Я портировал Hunchentoot на другие Lisp'ы отличные от LispWorks, Я украл код из ACL-COMPAT, KMRCL и trivial-sockets для таких платформо-зависимых вещей как сокеты и MP. (Впоследствии этот код был заменён кодом Bordeaux Threads и usocket.)

Части этой документации были подготовлены с помощью DOCUMENTATION-TEMPLATE, в процессе написания документации ни одно животное не пострадало.

Оригинал документации. Переводчик: Charlz_Klug. Дата: 2012.01.30.