1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : ejabberd_zlib.erl |
3 |
|
%%% Author : Alexey Shchepin <alexey@process-one.net> |
4 |
|
%%% Purpose : Interface to zlib |
5 |
|
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne |
9 |
|
%%% |
10 |
|
%%% This program is free software; you can redistribute it and/or |
11 |
|
%%% modify it under the terms of the GNU General Public License as |
12 |
|
%%% published by the Free Software Foundation; either version 2 of the |
13 |
|
%%% License, or (at your option) any later version. |
14 |
|
%%% |
15 |
|
%%% This program is distributed in the hope that it will be useful, |
16 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 |
|
%%% General Public License for more details. |
19 |
|
%%% |
20 |
|
%%% You should have received a copy of the GNU General Public License |
21 |
|
%%% along with this program; if not, write to the Free Software |
22 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%---------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(ejabberd_zlib). |
27 |
|
-author('alexey@process-one.net'). |
28 |
|
-xep([{xep, 138}, {version, "2.0"}]). |
29 |
|
-export([enable_zlib/3, disable_zlib/1, |
30 |
|
send/2, |
31 |
|
recv_data/2, |
32 |
|
setopts/2, |
33 |
|
sockname/1, peername/1, |
34 |
|
get_sockmod/1, |
35 |
|
controlling_process/2, |
36 |
|
close/1]). |
37 |
|
|
38 |
|
-ignore_xref([close/1, controlling_process/2, disable_zlib/1, peername/1, send/2, |
39 |
|
setopts/2, sockname/1]). |
40 |
|
|
41 |
|
-define(DEFLATE, 1). |
42 |
|
-define(INFLATE, 2). |
43 |
|
|
44 |
|
-record(zlibsock, {sockmod, socket, zlibport, inflate_size_limit = 0}). |
45 |
|
-type zlibsock() :: #zlibsock{}. |
46 |
|
|
47 |
|
-spec enable_zlib(ejabberd:sockmod(), zlibsock(), integer()) -> {ok, zlibsock()}. |
48 |
|
enable_zlib(SockMod, Socket, InflateSizeLimit) -> |
49 |
11 |
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of |
50 |
11 |
ok -> ok; |
51 |
:-( |
{error, already_loaded} -> ok; |
52 |
|
{error, OtherError} -> |
53 |
:-( |
erlang:error({cannot_load_ejabberd_zlib_drv, erl_ddll:format_error(OtherError)}) |
54 |
|
end, |
55 |
11 |
Port = open_port({spawn_driver, "ejabberd_zlib_drv"}, [binary]), |
56 |
11 |
{ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port, |
57 |
|
inflate_size_limit = InflateSizeLimit}}. |
58 |
|
|
59 |
|
-spec disable_zlib(zlibsock()) -> {ejabberd:sockmod(), inet:socket()}. |
60 |
|
disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) -> |
61 |
:-( |
port_close(Port), |
62 |
:-( |
{SockMod, Socket}. |
63 |
|
|
64 |
|
-spec recv_data(zlibsock(), Packet :: string()|binary()) -> {ok, _} | {error, _}. |
65 |
|
recv_data(#zlibsock{sockmod = SockMod, socket = Socket} = ZlibSock, Packet) -> |
66 |
49 |
case SockMod of |
67 |
|
gen_tcp -> |
68 |
5 |
recv_data2(ZlibSock, Packet); |
69 |
|
_ -> |
70 |
44 |
case SockMod:recv_data(Socket, Packet) of |
71 |
|
{ok, Packet2} -> |
72 |
44 |
recv_data2(ZlibSock, Packet2); |
73 |
|
Error -> |
74 |
:-( |
Error |
75 |
|
end |
76 |
|
end. |
77 |
|
|
78 |
|
recv_data2(ZlibSock, Packet) -> |
79 |
49 |
case catch recv_data1(ZlibSock, Packet) of |
80 |
|
{'EXIT', Reason} -> |
81 |
:-( |
{error, Reason}; |
82 |
|
Res -> |
83 |
49 |
Res |
84 |
|
end. |
85 |
|
|
86 |
|
-spec recv_data1(zlibsock(), iolist()) -> {'error', atom()} | {'ok', binary()}. |
87 |
|
recv_data1(#zlibsock{zlibport = Port, inflate_size_limit = SizeLimit} = _ZlibSock, Packet) -> |
88 |
49 |
case port_control(Port, SizeLimit bsl 2 + ?INFLATE, Packet) of |
89 |
|
<<0, In/binary>> -> |
90 |
48 |
{ok, In}; |
91 |
|
<<1, Error/binary>> -> |
92 |
1 |
{error, erlang:binary_to_existing_atom(Error, utf8)} |
93 |
|
end. |
94 |
|
|
95 |
|
-spec send(zlibsock(), iolist()) -> ok | {error, atom()}. |
96 |
|
send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}, |
97 |
|
Packet) -> |
98 |
40 |
case port_control(Port, ?DEFLATE, Packet) of |
99 |
|
<<0, Out/binary>> -> |
100 |
40 |
mongoose_metrics:update(global, [data, xmpp, sent, compressed_size], size(Out)), |
101 |
40 |
SockMod:send(Socket, Out); |
102 |
|
<<1, Error/binary>> -> |
103 |
:-( |
{error, erlang:binary_to_existing_atom(Error, utf8)}; |
104 |
|
_ -> |
105 |
:-( |
{error, deflate_error} |
106 |
|
end. |
107 |
|
|
108 |
|
|
109 |
|
setopts(#zlibsock{sockmod = SockMod, socket = Socket}, Opts) -> |
110 |
48 |
case SockMod of |
111 |
|
gen_tcp -> |
112 |
4 |
inet:setopts(Socket, Opts); |
113 |
|
_ -> |
114 |
44 |
SockMod:setopts(Socket, Opts) |
115 |
|
end. |
116 |
|
|
117 |
|
sockname(#zlibsock{sockmod = SockMod, socket = Socket}) -> |
118 |
:-( |
case SockMod of |
119 |
|
gen_tcp -> |
120 |
:-( |
inet:sockname(Socket); |
121 |
|
_ -> |
122 |
:-( |
SockMod:sockname(Socket) |
123 |
|
end. |
124 |
|
|
125 |
|
get_sockmod(#zlibsock{sockmod = SockMod}) -> |
126 |
9 |
SockMod. |
127 |
|
|
128 |
|
peername(#zlibsock{sockmod = SockMod, socket = Socket}) -> |
129 |
48 |
case SockMod of |
130 |
|
gen_tcp -> |
131 |
4 |
inet:peername(Socket); |
132 |
|
_ -> |
133 |
44 |
SockMod:peername(Socket) |
134 |
|
end. |
135 |
|
|
136 |
|
controlling_process(#zlibsock{sockmod = SockMod, socket = Socket}, Pid) -> |
137 |
:-( |
SockMod:controlling_process(Socket, Pid). |
138 |
|
|
139 |
|
close(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) -> |
140 |
11 |
SockMod:close(Socket), |
141 |
11 |
port_close(Port). |