@@ -111,6 +111,25 @@ wait_headers({headers_complete, Parser}, Client, Status, Headers) ->
111111 [hackney , Client # client .host , response_time ],
112112 ResponseTime ),
113113 HeadersList = hackney_headers_new :to_list (Headers ),
114+ CE = case proplists :get_value (compress , Client # client .options , false ) of
115+ true ->
116+ case hackney_headers_new :get_value (<<" content-encoding" >>, Headers , nil ) of
117+ nil -> nil ;
118+ C ->
119+ Z = zlib :open (),
120+ % % inflateInit2 (https://www.zlib.net/manual.html#Advanced)
121+ WindowBits = 15 + if C == <<" gzip" >> -> 16 ; true -> 0 end ,
122+ ok = zlib :inflateInit (Z , WindowBits ),
123+ ok = case erlang :function_exported (zlib , safeInflate , 2 ) of
124+ % % OTP-20.0.5 and later
125+ true -> ok ;
126+ % % OTP-18.0 and later
127+ false -> zlib :setBufSize (Z , 512 * 1024 )
128+ end ,
129+ {zlib ,Z }
130+ end ;
131+ false -> nil
132+ end ,
114133 TE = hackney_headers_new :get_value (<<" transfer-encoding" >>, Headers , nil ),
115134 CLen = case hackney_headers_new :lookup (" content-length" , Headers ) of
116135 [] -> undefined ;
@@ -122,6 +141,7 @@ wait_headers({headers_complete, Parser}, Client, Status, Headers) ->
122141 end ,
123142 Client2 = Client # client {parser = Parser ,
124143 headers = Headers ,
144+ ce = CE ,
125145 te = TE ,
126146 clen = CLen },
127147 {ok , Status , HeadersList , Client2 }.
@@ -147,17 +167,64 @@ stream_body(Client=#client{parser=Parser, clen=CLen, te=TE}) ->
147167stream_body (Data , # client {parser = Parser }= Client ) ->
148168 stream_body1 (hackney_http :execute (Parser , Data ), Client ).
149169
150- stream_body1 ({more , Parser , Buffer }, Client ) ->
170+ stream_body1 ({ok , Data , Parser }, Client = # client {ce = {zlib ,Z }}) ->
171+ stream_body2 (case stream_body_zlib (Z , Data ) of
172+ <<>> -> {more , Parser , <<>>};
173+ D when is_binary (D ) -> {ok , D , Parser };
174+ E -> {error ,E }
175+ end , Client );
176+ stream_body1 ({done , _Rest }, Client = # client {ce = {zlib ,_Z }}) ->
177+ stream_body1 (done , Client );
178+ stream_body1 (done , Client = # client {ce = {zlib ,Z }, parser = Parser }) ->
179+ stream_body2 (case stream_body_zlib (Z , <<>>) of
180+ done -> done ;
181+ D when is_binary (D ), size (D ) > 0 -> {ok , D , Parser };
182+ E -> {error ,E }
183+ end , Client );
184+ stream_body1 (Result , Client ) ->
185+ stream_body2 (Result , Client ).
186+
187+ stream_body_zlib (Z , Data ) ->
188+ case erlang :function_exported (zlib , safeInflate , 2 ) of
189+ true ->
190+ % % OTP-20.0.5 and later
191+ case zlib :safeInflate (Z , Data ) of
192+ {continue , []} when Data == <<>> ->
193+ data_error ;
194+ {finished , []} when Data == <<>> ->
195+ case (catch zlib :inflateEnd (Z )) of
196+ ok -> done ;
197+ _ -> data_error
198+ end ;
199+ {_ , Output } ->
200+ iolist_to_binary (Output )
201+ end ;
202+ false ->
203+ % % OTP-18.0 and later
204+ case zlib :inflateChunk (Z , Data ) of
205+ [] when Data == <<>> ->
206+ case (catch zlib :inflateEnd (Z )) of
207+ ok -> done ;
208+ _ -> data_error
209+ end ;
210+ {more , Decompressed } ->
211+ iolist_to_binary (Decompressed );
212+ Decompressed ->
213+ iolist_to_binary (Decompressed )
214+ end
215+ end .
216+
217+ stream_body2 ({more , Parser , Buffer }, Client ) ->
151218 stream_body_recv (Buffer , Client # client {parser = Parser });
152- stream_body1 ({ok , Data , Parser }, Client ) ->
219+ stream_body2 ({ok , Data , Parser }, Client ) ->
153220 {ok , Data , Client # client {parser = Parser }};
154- stream_body1 ({done , Rest }, Client ) ->
221+ stream_body2 ({done , Rest }, Client ) ->
155222 Client2 = end_stream_body (Rest , Client ),
156223 {done , Client2 };
157- stream_body1 (done , Client ) ->
224+ stream_body2 (done , Client ) ->
158225 Client2 = end_stream_body (<<>>, Client ),
159226 {done , Client2 };
160- stream_body1 (Error , _Client ) ->
227+ stream_body2 (Error , _Client ) ->
161228 Error .
162229
163230
@@ -277,6 +344,9 @@ skip_body(Client) ->
277344 {error , Reason } -> {error , Reason }
278345 end .
279346
347+ end_stream_body (Rest , Client = # client {ce = {zlib ,Z }}) ->
348+ catch zlib :close (Z ),
349+ end_stream_body (Rest , Client # client {ce = nil });
280350end_stream_body (Rest , Client0 ) ->
281351 Client = Client0 # client {response_state = done ,
282352 body_state = done ,
0 commit comments