Прочитав пост на хабре про онлайн чат для сайта через джаббер, мне стало интересно — а как оно работает и как такое можно сделать самому, без готовых приложений. В итоге у меня получилась очень простая заготовка «чата для сайта через джаббер». К сожалению у меня нет выделенного сервера с линуксом для тестов, поэтому был использован локальный компьютер с Win7 (и сервером Apache).
Как это вообще должно работать: пользователь заходит на сайт, и видит окошко, куда можно разговаривать. После того как пользователь послал сообщение, оно прилетает на указанный джаббер аккаунт. Получатель этого сообщения может написать ответ и оно придёт посетителю сайта.
Что для этого нужно:
JS не умеет создавать TCP соединения с другим сервером/клиентом, что необходимо для реализации джаббер-клиента. JS может посылать только HTTP запросы. Поэтому нужен специальный механизм, который позволит работать с TCP соединениями посредством HTTP. Это и есть BOSH.
По-умолчанию BOSH в Openfire включён и имеет адрес
httpd.conf:
Итак, всё очень просто, устанавливаем Openfire, создаём пользователя, например, site, и создаём тестовую страницу (в качестве основы я брал строфовский пример echobot). На странице есть поле, куда пользователь пишет своё сообщение, и поле с историей чата.
Пишем обработчик загрузки страницы, который будет логинится на джаббер сервер:
В обработчике onConnect мы добавляем «слушателя» на события, связанные с приходом сообщений (в случае успешного логина):
Обработчику onMessage приходят XML данные. Разбираем их что бы узнать текст сообщения, кто отправитель и т.д.:
Обработчик кнопки «Отправить» формирует XML с необходимыми данными и просит Strophe отправить их. В качестве принимающего используется пользователь admin:
В итоге получился очень простой набросок, позволяющий общаться по джабберу через веб-страницу. Мне было интересно узнать как это работает в принципе, поэтому я не стал копать дальше. Что-то мне подсказывает там есть куча подводных камней :)
Можно придумать ещё очень много усовершенствований. Например:
Как это вообще должно работать: пользователь заходит на сайт, и видит окошко, куда можно разговаривать. После того как пользователь послал сообщение, оно прилетает на указанный джаббер аккаунт. Получатель этого сообщения может написать ответ и оно придёт посетителю сайта.
Что для этого нужно:
- Jabber сервер, можно публичный, можно локальный. Я выбрал Openfire и установил его локально. Сервер должен поддерживать Bosh — XEP-0124: Bidirectional-streams Over Synchronous HTTP, об этом чуть позже.
- JS библиотека, которая будет реализовывать джаббер-клиент на сайте. Я взял Strophe. Это достаточно низкоуровневая библиотека, в которой нет функций типа «ПослатьСообщение(Куда, Текст)». Для достижения нужных действий нужно вручную составлять команды джаббер серверу (в XML). Удобные средства для создания XML в Strophe есть :)
BOSH
JS не умеет создавать TCP соединения с другим сервером/клиентом, что необходимо для реализации джаббер-клиента. JS может посылать только HTTP запросы. Поэтому нужен специальный механизм, который позволит работать с TCP соединениями посредством HTTP. Это и есть BOSH.
По-умолчанию BOSH в Openfire включён и имеет адрес
localhost:7070/http-bind
. Но если указать этот адрес при соединении, ничего не выходит. Проблема хорошо описана здесь, что бы заработало нужно написать редирект для апача и раскомментировать модули proxy_module и proxy_http_module:httpd.conf:
ProxyRequests Off ProxyPass /http-bind http://127.0.0.1:7070/http-bind/ ProxyPassReverse /http-bind http://127.0.0.1:7070/http-bind/
Чат на сайте
Итак, всё очень просто, устанавливаем Openfire, создаём пользователя, например, site, и создаём тестовую страницу (в качестве основы я брал строфовский пример echobot). На странице есть поле, куда пользователь пишет своё сообщение, и поле с историей чата.
Пишем обработчик загрузки страницы, который будет логинится на джаббер сервер:
$(function () { connection = new Strophe.Connection('/http-bind'); connection.connect('site@r1c', 'site', onConnect); });
В обработчике onConnect мы добавляем «слушателя» на события, связанные с приходом сообщений (в случае успешного логина):
function onConnect(status){ if (status == Strophe.Status.CONNECTING) { log('Strophe is connecting.'); } else if (status == Strophe.Status.CONNFAIL) { log('Strophe failed to connect.'); } else if (status == Strophe.Status.DISCONNECTING) { log('Strophe is disconnecting.'); } else if (status == Strophe.Status.DISCONNECTED) { log('Strophe is disconnected.'); } else if (status == Strophe.Status.CONNECTED) { log('Strophe is connected, ' + connection.jid); connection.addHandler(onMessage, null, 'message', null, null, null); connection.send($pres().tree()); } }
Обработчику onMessage приходят XML данные. Разбираем их что бы узнать текст сообщения, кто отправитель и т.д.:
function onMessage(msg) { var to = msg.getAttribute('to'); var from = msg.getAttribute('from'); var type = msg.getAttribute('type'); var elems = msg.getElementsByTagName('body'); if (type == "chat" && elems.length > 0) { var body = elems[0]; AddText(Strophe.getText(body), 'in'); } // we must return true to keep the handler alive. // returning false would remove it after it finishes. return true; }
Обработчик кнопки «Отправить» формирует XML с необходимыми данными и просит Strophe отправить их. В качестве принимающего используется пользователь admin:
function Send(message) { var msg = $msg({to: 'admin@r1c', from: connection.jid, type: 'chat'}).c('body').t(document.URL + '\n' + message); connection.send(msg.tree()); AddText(message, 'out'); $('input#message').val(''); }
Итог
В итоге получился очень простой набросок, позволяющий общаться по джабберу через веб-страницу. Мне было интересно узнать как это работает в принципе, поэтому я не стал копать дальше. Что-то мне подсказывает там есть куча подводных камней :)
Можно придумать ещё очень много усовершенствований. Например:
- Нормальный интерфейс чата на сайте :)
- Автоматическая регистрация новых пользователей на джаббер-сервере, если в этом есть необходимость. Для этого можно использовать In-band registration — регистрация через XML команды джаббера, а не через веб-интерфейс.
- Сохранение состояния чата при переходе между разными страницами сайта
- Сохранение истории чата на сервере
- и т.д.