开发一个Ejabberd HTTP模块

概述

如何开发一个Ejabberd模块, 本文基于一个实例演示了如何开发一个Ejabberd模块, 以及模块的基本结构.

HTTP模块的核心函数是process(Paths,Requst)处理函数, 接受一个路径列表, 和一个请求作为参数. 返回值为一个三元组{ResponseCode, ResponseHeaders, ResponseBody}, 分别表示响应码, 响应头和响应体. 其中:

  1. ResponseCode为一个整数值, 例如200
  2. ResponseHeaders为一个二元组列表, 例如[{<<"Content-Type">>, <<"text/html; charset=utf-8">>}],表示要设置的HTTP响应头信息.
  3. ResponseBody为一个类似<<"Content">>二进制字符串, 返回给客户端的实际内容.

具体细节可参考ejabberd_http.erl模块的make_xhtml_output/4函数.

下面是一个process/4函数的例子, 该函数用于获取当前在线用户数, 支持跨域资源共享(CORS):

1
2
3
4
5
6
7
8
9
10
process(_LocalPath, _Request) ->
ConnectedUsersNumber = ejabberd_sm:connected_users_number(),
Json = jiffy:encode({[
{connected_users_number, ConnectedUsersNumber}
]}),

{200, [
{<<"Access-Control-Allow-Origin">>, <<"http://www.example.com">>},
{<<"Access-Control-Allow-Headers">>, <<"Content-Type,X-Requested-With">>},
{<<"Content-Type">>, <<"application/json; charset=utf-8">>}
], Json}.

在线用户数也可通过ejabberdctl connected_users_number获取.

基本模块结构

1
%% Module name (has to match with the filename)
-module(mod_custom).
%% Module author
-author('Gregor Uhlenheuer').
%% Module version
-vsn('1.0').
%% Debug flag
-define(EJABBERD_DEBUG, true).
%% Implement the OTP gen_mod behavior
-behavior(gen_mod).
%% Module exports
-export([start/2, stop/1, process/2]).
%%
%% INCLUDES
%%
%% base ejabberd headers
-include("ejabberd.hrl").
%% ejabberd compatibility functions
-include("jlib.hrl").
%% ejabberd HTTP headers
-include("web/ejabberd_http.hrl").
%% initialization function
start(_Host, _Opts) ->
    ok.
%% function on module unload
stop(_Host) ->
    ok.
%% process any request to "/sockets"
process(["sockets"], _Request) ->
    % FIXME: implementation goes here
    "Not implemented yet";
%% process all remaining requests
process(_Page, _Request) ->
    % FIXME: implementation goes here
    "Fallback result".

编译模块

1
erlc -I ../ejabberd/src \
     -I /lib64/ejabberd/include \
     -pa ../ejabberd/src \
     mod_custom.erl

模块的.beam文件需要放在ejabberd的ebin目录下

配置模块

  • 在主配置文件中添加模块配置

原先的主配置文件中配置为

1
{5280, ejabberd_http, [http_poll, web_admin]}

修改后的为

1
% this will probably look like this
{5280, ejabberd_http, [http_poll, web_admin,
        {request_handlers, [
            % your request handler will respond to anything like:
            % http://example.com:5280/custom/
            {["custom"], mod_custom}
        ]}
    ]}

模块的在线更新

当我们修改了模块的代码时,如何更新模块代码而不用重启Ejabberd服务器呢?

ejabberdctl 命令提供了一个update子命令, 如下:

1
ejabberdctl update mod_name

执行上述命令行即可动态跟新模块代码.

修订

  • 2014-09-27

    • 模块编译
      模块文件放到$EJABBERD_SOURCE/src目录下,然后make && make install && ejabberdctl restart
    • 模块配置
      YAML格式的配置方式(ejabberd 13.10以后)
      1
      -
          port: 5280
          module: ejabberd_http
          request_handlers:
            "custom": mod_custom %% http://example.com:5280/custom/
  • 2015-03-17

    • 自定义模块可以放在在$EJABBERD_SOURCE/src/modules子目录下, modules子目录名称可以任意取名
    • Ejabberd的配置文件在13.x后修改为使用YAML格式,上述HTTP模块的配置修改为如下:
      1
      -
        port: 5280
        module: ejabberd_http
          request_handlers:
            "/custom": mod_custom
        web_admin: true
        http_poll: true
  • 2015-11-11

    • 增加概述部分,说明HTTP模块的process/4函数接受的参数和返回值.
  • 2015-11-12
    • 增加动态Hot swap模块的方式

      参考资料

  1. http://uhlenheuer.net/posts/2013-01-16-writing_an_ejabberd_module.html
  2. http://sacharya.com/writing-ejabberd-modules/
  3. http://jasonrowe.com/2011/12/30/ejabberd-offline-messages