Понадобился мне недавно pingback-сервер, и я, посмотрев на django-pingback, в приступе NIH-синдрома написал своё приложение — pingdjack. За название спасибо Михаилу Лукьянченко.
Отмазка
Более-менее реальные претензии к django-pingback у меня тоже есть. Хотя я сразу признаюсь, что не пробовал во что бы то ни стало с ней ужиться, просто код почитал. Претензии такие:
- Зависит от django-xmlrpc, хотя на мой взгляд XML-RPC в Питоне и так достаточно простая вещь, чтобы не городить для неё Джанго-специфичного слоя.
- Использование обобщённого XML-RPC-слоя тянет за собой необходимость делать в нём регистрацию pingback-хендлеров или регистрировать middleware, которая будет делать это за вас.
- В качестве цели пинга регистрируются объекты моделей, а не любой произвольный URL сервера.
- У приложения есть свои модели, и как я понимаю, пингбеки оно хранит как само хочет, а не так, как мне надо.
- Библиотека не ищет цитату из поста и автора так, как мне давно хотелось.
Pingdjack
Получилась библиотечка из двух модулей: клиента и сервера. Единственная внешняя зависимость — html5lib, без неё нынче никуда :-).
Клиента я просто выдрал из Cicero. Он состоит из трёх функций:
external_urls
, которая ищет внешние ссылки в куске HTML'аping
, которая пингует один URL от имени другогоping_external_urls
, которая соединяет одно с другим, пингуя все внешние ссылки из куска HTML'а
Когда их вызывать — дело того приложения, которое делает пинги.
Серверная часть написана с нуля.
Там есть вьюха server_view
, которая подключается в любое место urlconf'а, какое вам хочется. Она парсит XML-RPC-запрос и вызывает всякие проверки и парсинг HTML'а источника. В частности:
- проверяется, что приёмный URL действительно принадлежит вашему серверу (опционально с уточнением root-пути, под которым он должен лежать)
- путь приёмного URL'а ресолвится во вьюху, чтобы во-первых не беспокоить приложение несуществующими URL'ами, а во-вторых, именно по разресолвленной view и аргументам приложение будет понимать, куда пришёл pingback
- с URL'а-источника скачивается кусок HTML'а, в котором:
- ищется URL приёмника, чтобы убедиться, что оттуда действительно есть ссылка сюда
- вокруг найденного URL'а ищется правильный HTML-контейнер, из которого берётся полный текст цитаты
- вокруг цитаты ищется тег
<address>
с именем автора или, если его нет,<title>
всей страницы
И только если всё правильно нашлось, она посылает сигнал received
, в который передаёт кучу всего, что напарсилось. Этот сигнал и есть единственная связь библиотеки с приложением, которое её использует. На сигнал надо повесить обработчик и написать то, что вы хотите сделать с пингами. Вот, например, как может себя вести блог, который хочет добавлять пинги в виде комментариев:
def handle_pingback(sender, source_url, view, args, author, excerpt, **kwargs):
if view != post:
return # интересны только пинги на посты
post = Post.objects.get(slug=args[0])
post.comment_set.create(
text = excerpt,
url = source_url,
author = author,
)
pingdjack.received.connect(handle_pingback)
Пользуйтесть на здоровье!
P.S. Кстати, библиотечка вышла на каких-то 158 строк питоньего кода.
Комментарии: 7
Круто, спасибо!
Ещё на стороне сервера надо не забывать проверять был ли с такого-то урла уже ping;-) Хотя при такой системе с сигналами, если он и был уже, то об том факте нельзя будет указать клиенту.
Интересно, что теоретически защиту от дублей можно делать и на стороне клиента, и на стороне сервера. Хотя, если подумать над спеком pingback'а, то тот факт, что он не запрещает явно многократное пингование, говорит о том, что скорее серверу надо запоминать, какие пинги он уже принимал. Клиенту про это знать и не надо: пусть пингует.
Я всё раздумываю, включить ли этот код в саму библиотеку или оставить это на совесть того кода, который ею пользуется. От включения в библиотеку держит то, что тогда она сразу получает файл models.py, должна быть включена в
INSTALLED_APPS
и от этого начинает выглядеть более сложной.Я таки пошёл почитать. И нашёл вот это:
То есть это, без вариантов, дело сервера, и у него даже есть способ ответить про это клиенту. Хотя и опционально. Придётся дописывать :-)
Я про этот статус ответа и говорю. Но по большому счету от бесполезен (как и большинство остальных).
Если клиент не сохраняет информацию об уже отпингованных урлах, то у него от прихода такого статуса ничего не изменится. А если колиент как-то у себя хранит уже обработанные урлы, то он не будет и так пинговать их повторно.
Поэтому мне кажется, что раз нет легкого пути из обработчика сигнала вернуть некий статус этой обработки (ведь внутри него ты можешь найти комментарий с данным урлом и определить что пинг уже был), то пусть сервер будет в неведении относительно повторных пингов.
Почему нет? Результаты из обработчиков сигнала вполне возвращаются. Но я не хочу вообще грузить этой заботой обработчик сигнала. Ведь все остальные ошибочные ситуации pingdjack отлавливает и вроде бы предоставляет своему пользователю хороший сервис: "я позову сигнал, когда уже точно всё нормально". Но вот дубли — не отлавливает. Нехорошо.
Я как-то об этом забыл. Тогда вообще проблемы нет.
Тут меня смущает то, что информация об пинге будет храниться в нескольких местах (минимум в двух: пингджеке и комментариях), что при слабой их связности может породить какие-нибудь плохие эффекты. Хотя это конечно уже совсем придирка.