Mercurial > prosody-modules
comparison mod_compression/mod_compression.lua @ 2482:c96a53b0b820
mod_compression: Import from Prosody, revision 0c69305ab525
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sun, 05 Feb 2017 14:40:51 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2481:854aaa1f01b2 | 2482:c96a53b0b820 |
---|---|
1 -- Prosody IM | |
2 -- Copyright (C) 2009-2012 Tobias Markmann | |
3 -- | |
4 -- This project is MIT/X11 licensed. Please see the | |
5 -- COPYING file in the source package for more information. | |
6 -- | |
7 | |
8 local st = require "util.stanza"; | |
9 local zlib = require "zlib"; | |
10 local pcall = pcall; | |
11 local tostring = tostring; | |
12 | |
13 local xmlns_compression_feature = "http://jabber.org/features/compress" | |
14 local xmlns_compression_protocol = "http://jabber.org/protocol/compress" | |
15 local xmlns_stream = "http://etherx.jabber.org/streams"; | |
16 local compression_stream_feature = st.stanza("compression", {xmlns=xmlns_compression_feature}):tag("method"):text("zlib"):up(); | |
17 local add_filter = require "util.filters".add_filter; | |
18 | |
19 local compression_level = module:get_option_number("compression_level", 7); | |
20 | |
21 if not compression_level or compression_level < 1 or compression_level > 9 then | |
22 module:log("warn", "Invalid compression level in config: %s", tostring(compression_level)); | |
23 module:log("warn", "Module loading aborted. Compression won't be available."); | |
24 return; | |
25 end | |
26 | |
27 module:hook("stream-features", function(event) | |
28 local origin, features = event.origin, event.features; | |
29 if not origin.compressed and origin.type == "c2s" then | |
30 features:add_child(compression_stream_feature); | |
31 end | |
32 end); | |
33 | |
34 module:hook("s2s-stream-features", function(event) | |
35 local origin, features = event.origin, event.features; | |
36 if not origin.compressed and origin.type == "s2sin" then | |
37 features:add_child(compression_stream_feature); | |
38 end | |
39 end); | |
40 | |
41 -- Hook to activate compression if remote server supports it. | |
42 module:hook_stanza(xmlns_stream, "features", | |
43 function (session, stanza) | |
44 if not session.compressed and session.type == "s2sout" then | |
45 -- does remote server support compression? | |
46 local comp_st = stanza:get_child("compression", xmlns_compression_feature); | |
47 if comp_st then | |
48 -- do we support the mechanism | |
49 for a in comp_st:childtags("method") do | |
50 local algorithm = a:get_text(); | |
51 if algorithm == "zlib" then | |
52 session.sends2s(st.stanza("compress", {xmlns=xmlns_compression_protocol}):tag("method"):text("zlib")) | |
53 session.log("debug", "Enabled compression using zlib.") | |
54 return true; | |
55 end | |
56 end | |
57 session.log("debug", "Remote server supports no compression algorithm we support.") | |
58 end | |
59 end | |
60 end | |
61 , 250); | |
62 | |
63 | |
64 -- returns either nil or a fully functional ready to use inflate stream | |
65 local function get_deflate_stream(session) | |
66 local status, deflate_stream = pcall(zlib.deflate, compression_level); | |
67 if status == false then | |
68 local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); | |
69 (session.sends2s or session.send)(error_st); | |
70 session.log("error", "Failed to create zlib.deflate filter."); | |
71 module:log("error", "%s", tostring(deflate_stream)); | |
72 return | |
73 end | |
74 return deflate_stream | |
75 end | |
76 | |
77 -- returns either nil or a fully functional ready to use inflate stream | |
78 local function get_inflate_stream(session) | |
79 local status, inflate_stream = pcall(zlib.inflate); | |
80 if status == false then | |
81 local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); | |
82 (session.sends2s or session.send)(error_st); | |
83 session.log("error", "Failed to create zlib.inflate filter."); | |
84 module:log("error", "%s", tostring(inflate_stream)); | |
85 return | |
86 end | |
87 return inflate_stream | |
88 end | |
89 | |
90 -- setup compression for a stream | |
91 local function setup_compression(session, deflate_stream) | |
92 add_filter(session, "bytes/out", function(t) | |
93 local status, compressed, eof = pcall(deflate_stream, tostring(t), 'sync'); | |
94 if status == false then | |
95 module:log("warn", "%s", tostring(compressed)); | |
96 session:close({ | |
97 condition = "undefined-condition"; | |
98 text = compressed; | |
99 extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed"); | |
100 }); | |
101 return; | |
102 end | |
103 return compressed; | |
104 end); | |
105 end | |
106 | |
107 -- setup decompression for a stream | |
108 local function setup_decompression(session, inflate_stream) | |
109 add_filter(session, "bytes/in", function(data) | |
110 local status, decompressed, eof = pcall(inflate_stream, data); | |
111 if status == false then | |
112 module:log("warn", "%s", tostring(decompressed)); | |
113 session:close({ | |
114 condition = "undefined-condition"; | |
115 text = decompressed; | |
116 extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed"); | |
117 }); | |
118 return; | |
119 end | |
120 return decompressed; | |
121 end); | |
122 end | |
123 | |
124 module:hook("stanza/http://jabber.org/protocol/compress:compressed", function(event) | |
125 local session = event.origin; | |
126 | |
127 if session.type == "s2sout" then | |
128 session.log("debug", "Activating compression...") | |
129 -- create deflate and inflate streams | |
130 local deflate_stream = get_deflate_stream(session); | |
131 if not deflate_stream then return true; end | |
132 | |
133 local inflate_stream = get_inflate_stream(session); | |
134 if not inflate_stream then return true; end | |
135 | |
136 -- setup compression for session.w | |
137 setup_compression(session, deflate_stream); | |
138 | |
139 -- setup decompression for session.data | |
140 setup_decompression(session, inflate_stream); | |
141 session:reset_stream(); | |
142 session:open_stream(session.from_host, session.to_host); | |
143 session.compressed = true; | |
144 return true; | |
145 end | |
146 end); | |
147 | |
148 module:hook("stanza/http://jabber.org/protocol/compress:failure", function(event) | |
149 local err = event.stanza:get_child(); | |
150 (event.origin.log or module._log)("warn", "Compression setup failed (%s)", err and err.name or "unknown reason"); | |
151 return true; | |
152 end); | |
153 | |
154 module:hook("stanza/http://jabber.org/protocol/compress:compress", function(event) | |
155 local session, stanza = event.origin, event.stanza; | |
156 | |
157 if session.type == "c2s" or session.type == "s2sin" then | |
158 -- fail if we are already compressed | |
159 if session.compressed then | |
160 local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); | |
161 (session.sends2s or session.send)(error_st); | |
162 session.log("debug", "Client tried to establish another compression layer."); | |
163 return true; | |
164 end | |
165 | |
166 -- checking if the compression method is supported | |
167 local method = stanza:get_child_text("method"); | |
168 if method == "zlib" then | |
169 session.log("debug", "zlib compression enabled."); | |
170 | |
171 -- create deflate and inflate streams | |
172 local deflate_stream = get_deflate_stream(session); | |
173 if not deflate_stream then return true; end | |
174 | |
175 local inflate_stream = get_inflate_stream(session); | |
176 if not inflate_stream then return true; end | |
177 | |
178 (session.sends2s or session.send)(st.stanza("compressed", {xmlns=xmlns_compression_protocol})); | |
179 session:reset_stream(); | |
180 | |
181 -- setup compression for session.w | |
182 setup_compression(session, deflate_stream); | |
183 | |
184 -- setup decompression for session.data | |
185 setup_decompression(session, inflate_stream); | |
186 | |
187 session.compressed = true; | |
188 elseif method then | |
189 session.log("debug", "%s compression selected, but we don't support it.", tostring(method)); | |
190 local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method"); | |
191 (session.sends2s or session.send)(error_st); | |
192 else | |
193 (session.sends2s or session.send)(st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed")); | |
194 end | |
195 return true; | |
196 end | |
197 end); | |
198 |