@@ -19,6 +19,8 @@ defmodule Tesla.Middleware.Compression do
19
19
- `:format` - request compression format, `"gzip"` (default) or `"deflate"`
20
20
"""
21
21
22
+ require Logger
23
+
22
24
@ behaviour Tesla.Middleware
23
25
24
26
@ impl Tesla.Middleware
@@ -49,6 +51,26 @@ defmodule Tesla.Middleware.Compression do
49
51
end
50
52
end
51
53
54
+ defmacrop brotli_loaded? do
55
+ if Code . ensure_loaded? ( :brotli ) do
56
+ true
57
+ else
58
+ quote do
59
+ Code . ensure_loaded? ( :brotli )
60
+ end
61
+ end
62
+ end
63
+
64
+ defmacrop ezstd_loaded? do
65
+ if Code . ensure_loaded? ( :ezstd ) do
66
+ true
67
+ else
68
+ quote do
69
+ Code . ensure_loaded? ( :ezstd )
70
+ end
71
+ end
72
+ end
73
+
52
74
defp compress_body ( body , "gzip" ) , do: :zlib . gzip ( body )
53
75
defp compress_body ( body , "deflate" ) , do: :zlib . zip ( body )
54
76
@@ -61,13 +83,84 @@ defmodule Tesla.Middleware.Compression do
61
83
def decompress ( { :error , reason } ) , do: { :error , reason }
62
84
63
85
def decompress ( env ) do
86
+ codecs = compression_algorithms ( Tesla . get_header ( env , "content-encoding" ) )
87
+ { decompressed_body , unknown_codecs } = decompress_body ( codecs , env . body , [ ] )
88
+
89
+ env
90
+ |> put_decompressed_body ( decompressed_body )
91
+ |> put_or_delete_content_encoding ( unknown_codecs )
92
+ end
93
+
94
+ defp put_or_delete_content_encoding ( env , [ ] ) do
95
+ Tesla . delete_header ( env , "content-encoding" )
96
+ end
97
+
98
+ defp put_or_delete_content_encoding ( env , unknown_codecs ) do
99
+ Tesla . put_header ( env , "content-encoding" , Enum . join ( unknown_codecs , ", " ) )
100
+ end
101
+
102
+ defp decompress_body ( [ gzip | rest ] , body , acc ) when gzip in [ "gzip" , "x-gzip" ] do
103
+ decompress_body ( rest , :zlib . gunzip ( body ) , acc )
104
+ end
105
+
106
+ defp decompress_body ( [ "br" | rest ] , body , acc ) do
107
+ if brotli_loaded? ( ) do
108
+ { :ok , decompressed } = :brotli . decode ( body )
109
+ decompress_body ( rest , decompressed , acc )
110
+ else
111
+ Logger . debug ( ":brotli library not loaded, skipping brotli decompression" )
112
+ decompress_body ( rest , body , [ "br" | acc ] )
113
+ end
114
+ end
115
+
116
+ defp decompress_body ( [ "zstd" | rest ] , body , acc ) do
117
+ if ezstd_loaded? ( ) do
118
+ decompress_body ( rest , :ezstd . decompress ( body ) , acc )
119
+ else
120
+ Logger . debug ( ":ezstd library not loaded, skipping zstd decompression" )
121
+ decompress_body ( rest , body , [ "zstd" | acc ] )
122
+ end
123
+ end
124
+
125
+ defp decompress_body ( [ "identity" | rest ] , body , acc ) do
126
+ decompress_body ( rest , body , acc )
127
+ end
128
+
129
+ defp decompress_body ( [ codec | rest ] , body , acc ) do
130
+ Logger . debug ( "algorithm #{ inspect ( codec ) } is not supported" )
131
+ decompress_body ( rest , body , [ codec | acc ] )
132
+ end
133
+
134
+ defp decompress_body ( [ ] , body , acc ) do
135
+ { body , acc }
136
+ end
137
+
138
+ defp compression_algorithms ( nil ) do
139
+ [ ]
140
+ end
141
+
142
+ defp compression_algorithms ( value ) do
143
+ value
144
+ |> String . downcase ( )
145
+ |> String . split ( "," , trim: true )
146
+ |> Enum . map ( & String . trim / 1 )
147
+ |> Enum . reverse ( )
148
+ end
149
+
150
+ defp put_decompressed_body ( env , body ) do
151
+ content_length =
152
+ body
153
+ |> byte_size ( )
154
+ |> to_string ( )
155
+
64
156
env
65
- |> Tesla . put_body ( decompress_body ( env . body , Tesla . get_header ( env , "content-encoding" ) ) )
157
+ |> Tesla . put_body ( body )
158
+ |> Tesla . put_header ( "content-length" , content_length )
66
159
end
67
160
68
161
defp decompress_body ( << 31 , 139 , 8 , _ :: binary >> = body , "gzip" ) , do: :zlib . gunzip ( body )
69
162
defp decompress_body ( body , "deflate" ) , do: :zlib . unzip ( body )
70
- defp decompress_body ( body , _content_encoding ) , do: body
163
+ defp decompress_body ( _body , _content_encoding ) , do: nil
71
164
end
72
165
73
166
defmodule Tesla.Middleware.CompressRequest do
0 commit comments