faraday-gzip 3.0.4 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5183c6f188272ea7e213adf03bb2d637c348e9f273b1b8829255f135a95b8c92
4
- data.tar.gz: ce73797d7aa191ef07c376af0193ea704a653237146affec2806031194d064c7
3
+ metadata.gz: 838c02901688f672d7a94781a50997dc0dd82d9ea8f00addafc7e610cbf2c230
4
+ data.tar.gz: 6719045f1cccd8e339e0f97aea37b221bf61152c679248d2428ae92ea96a7a7c
5
5
  SHA512:
6
- metadata.gz: 8f9add78a8ff2c274ef021ae4e8ebfc1f525847ba4d05ca6ccf52cba1bc9f9d891e4627a354fdbf724393cb420e1225c33521d55f15f0c292490c8466e6ba202
7
- data.tar.gz: 2edf0cd82a52d2bf33d9024ed3363b4735dc7a7645f847bf1aca250947d14663d696d2bc64bb10fc83019a677a2b701d933766ed1afcc7f76d554a30b4e6c5f9
6
+ metadata.gz: d096aedb098363e777d5d818f8d050ed1c87df5edb032c26cd761976ed052baa92d01333665b2f72daffbdc0cea75606f3eeef8f0616140088cba5281a640ea1
7
+ data.tar.gz: 50adf03a07cc7968e26b44f276ddbdf6c087c86f33bcff589ba368b5ed557e043e9fc21500bb9f6999e5e7148e52bf4fabec42ae7b8c322807207dd7bbf45307
data/CHANGELOG.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.1.0 (05-Jan-2026)
4
+
5
+ * Improve handling of edge cases and malformed `Content-Encoding` headers
6
+ * Support multiple encodings and respect `identity` responses
7
+ * Avoid modifying streaming and non-string response bodies
8
+ * Normalize response headers after decompression
9
+ * Update and expand test coverage, test with Ruby 4.0
10
+
3
11
  ## 3.0.4 (06-Apr-2025)
4
12
 
5
- * Require StringIO that might not always be readily available
13
+ * Require `StringIO` that might not always be readily available
6
14
 
7
15
  ## 3.0.3 (25-Feb-2025)
8
16
 
data/README.md CHANGED
@@ -3,12 +3,14 @@
3
3
  ![CI](https://github.com/bodrovis/faraday-gzip/actions/workflows/ci.yaml/badge.svg)
4
4
  [![Gem](https://img.shields.io/gem/v/faraday-gzip.svg?style=flat-square)](https://rubygems.org/gems/faraday-gzip)
5
5
  ![Gem Total Downloads](https://img.shields.io/gem/dt/faraday-gzip)
6
+ [![Maintainability](https://qlty.sh/gh/bodrovis/projects/faraday-gzip/maintainability.svg)](https://qlty.sh/gh/bodrovis/projects/faraday-gzip)
7
+ [![Code Coverage](https://qlty.sh/gh/bodrovis/projects/faraday-gzip/coverage.svg)](https://qlty.sh/gh/bodrovis/projects/faraday-gzip)
6
8
 
7
- The `Gzip` middleware for Faraday 1 and 2 adds the necessary `Accept-Encoding` headers and automatically decompresses the response. If the "Accept-Encoding" header isn't set in the request, it defaults to `gzip,deflate` and appropriately handles the server's compressed response. This functionality resembles what Ruby does internally in `Net::HTTP#get`. If [Brotli](https://github.com/miyucy/brotli) is included in your Gemfile, the middleware also adds `br` to the header for Brotli support.
9
+ The `Gzip` middleware for Faraday 1 and 2 adds appropriate `Accept-Encoding` request headers and automatically decompresses supported response bodies (`gzip`, `deflate`, and optionally `br`). If the `Accept-Encoding` header is not explicitly set, it defaults to `gzip,deflate` and includes `br` when [Brotli](https://github.com/miyucy/brotli) support is available. The middleware safely handles multiple and malformed `Content-Encoding` headers, and avoids modifying unsupported or streaming response bodies. This behavior is similar in spirit to Ruby's internal handling in `Net::HTTP#get`, while remaining conservative to preserve compatibility with Faraday adapters.
8
10
 
9
11
  ## Prerequisites
10
12
 
11
- * faraday-gzip v3 supports only Faraday v2 and is tested with Ruby 3.0+ and JRuby 9.4
13
+ * faraday-gzip v3 supports only Faraday v2 and is tested with Ruby 3.0+ and JRuby 9.4+
12
14
  * [faraday-gzip v2](https://github.com/bodrovis/faraday-gzip/tree/v2) supports Faraday v1 and v2 and is tested with Ruby 2.7+ and JRuby 9.4.
13
15
 
14
16
  ## Installation
@@ -13,6 +13,11 @@ module Faraday
13
13
  module Gzip
14
14
  # Faraday middleware for decompression
15
15
  class Middleware < Faraday::Middleware
16
+ ACCEPT_ENCODING = 'Accept-Encoding'
17
+ CONTENT_ENCODING = 'Content-Encoding'
18
+ CONTENT_LENGTH = 'Content-Length'
19
+ IDENTITY = 'identity'
20
+
16
21
  # System method required by Faraday
17
22
  def self.optional_dependency(lib = nil)
18
23
  lib ? require(lib) : yield
@@ -31,9 +36,6 @@ module Faraday
31
36
  encodings
32
37
  end
33
38
 
34
- ACCEPT_ENCODING = 'Accept-Encoding'
35
- CONTENT_ENCODING = 'Content-Encoding'
36
- CONTENT_LENGTH = 'Content-Length'
37
39
  SUPPORTED_ENCODINGS = supported_encodings.join(',').freeze
38
40
 
39
41
  # Main method to process the response
@@ -47,21 +49,40 @@ module Faraday
47
49
 
48
50
  # Finds a proper processor
49
51
  def find_processor(response_env)
50
- if empty_body?(response_env)
51
- ->(body) { raw_body(body) }
52
- else
53
- processors[response_env[:response_headers][CONTENT_ENCODING]]
54
- end
52
+ body = response_env[:body]
53
+
54
+ encodings = parse_content_encoding(
55
+ response_env[:response_headers][CONTENT_ENCODING]
56
+ )
57
+ return nil if encodings.empty? || encodings.include?(IDENTITY)
58
+
59
+ # If body is nil/empty, we still want to normalize headers:
60
+ # Content-Encoding is meaningless without an encoded body.
61
+ return ->(b) { b } if body_nil_or_empty?(body)
62
+
63
+ chain = processor_chain(encodings)
64
+ return nil if chain.empty?
65
+
66
+ ->(b) { chain.reduce(b) { |acc, fn| fn.call(acc) } }
55
67
  end
56
68
 
57
69
  # Calls the proper processor to decompress body
58
70
  def reset_body(env, processor)
59
71
  return if processor.nil?
60
72
 
61
- env[:body] = processor.call(env[:body])
62
- env[:response_headers].delete(CONTENT_ENCODING)
73
+ body = env[:body]
74
+ headers = env[:response_headers]
75
+
76
+ # Don't touch streaming / IO-like bodies.
77
+ return unless body.is_a?(String) || body_nil_or_empty?(body)
63
78
 
64
- env[:response_headers][CONTENT_LENGTH] = env[:body].nil? ? 0 : env[:body].length
79
+ if body.is_a?(String)
80
+ env[:body] = processor.call(body)
81
+ headers[CONTENT_LENGTH] = env[:body].bytesize
82
+ end
83
+
84
+ # Normalize encoding header even for nil/empty body.
85
+ headers.delete(CONTENT_ENCODING)
65
86
  end
66
87
 
67
88
  # Process gzip
@@ -95,23 +116,38 @@ module Faraday
95
116
  Brotli.inflate(body)
96
117
  end
97
118
 
98
- # Do not process anything, leave body as is
99
- def raw_body(body)
100
- body
119
+ private
120
+
121
+ def body_nil_or_empty?(body)
122
+ return true if body.nil?
123
+ return body.empty? if body.respond_to?(:empty?)
124
+ # rubocop:disable Style/ZeroLengthPredicate
125
+ return body.size.zero? if body.respond_to?(:size)
126
+ # rubocop:enable Style/ZeroLengthPredicate
127
+
128
+ false
101
129
  end
102
130
 
103
- private
131
+ def parse_content_encoding(value)
132
+ return [] if value.nil?
133
+
134
+ value.to_s
135
+ .split(',')
136
+ .map { |v| v.strip.downcase }
137
+ .reject(&:empty?)
138
+ end
104
139
 
105
- def empty_body?(response_env)
106
- response_env[:body].nil? || response_env[:body].empty?
140
+ # Decode in reverse order of application:
141
+ # "gzip, br" => br -> gzip
142
+ def processor_chain(encodings)
143
+ encodings.reverse.filter_map { |enc| processors[enc] }
107
144
  end
108
145
 
109
- # Method providing the processors
110
146
  def processors
111
147
  @processors ||= {
112
148
  'gzip' => ->(body) { uncompress_gzip(body) },
113
149
  'deflate' => ->(body) { inflate(body) },
114
- 'br' => ->(body) { brotli_inflate(body) }
150
+ 'br' => (BROTLI_SUPPORTED ? ->(body) { brotli_inflate(body) } : nil)
115
151
  }
116
152
  end
117
153
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Faraday
4
4
  module Gzip
5
- VERSION = '3.0.4'
5
+ VERSION = '3.1.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday-gzip
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.4
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Krukowski
@@ -43,20 +43,6 @@ dependencies:
43
43
  - - "~>"
44
44
  - !ruby/object:Gem::Version
45
45
  version: '3.0'
46
- - !ruby/object:Gem::Dependency
47
- name: bundler
48
- requirement: !ruby/object:Gem::Requirement
49
- requirements:
50
- - - "~>"
51
- - !ruby/object:Gem::Version
52
- version: '2.0'
53
- type: :development
54
- prerelease: false
55
- version_requirements: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '2.0'
60
46
  - !ruby/object:Gem::Dependency
61
47
  name: rake
62
48
  requirement: !ruby/object:Gem::Requirement
@@ -105,42 +91,42 @@ dependencies:
105
91
  requirements:
106
92
  - - "~>"
107
93
  - !ruby/object:Gem::Version
108
- version: '1.32'
94
+ version: '1.82'
109
95
  type: :development
110
96
  prerelease: false
111
97
  version_requirements: !ruby/object:Gem::Requirement
112
98
  requirements:
113
99
  - - "~>"
114
100
  - !ruby/object:Gem::Version
115
- version: '1.32'
101
+ version: '1.82'
116
102
  - !ruby/object:Gem::Dependency
117
103
  name: rubocop-performance
118
104
  requirement: !ruby/object:Gem::Requirement
119
105
  requirements:
120
106
  - - "~>"
121
107
  - !ruby/object:Gem::Version
122
- version: '1.0'
108
+ version: '1.26'
123
109
  type: :development
124
110
  prerelease: false
125
111
  version_requirements: !ruby/object:Gem::Requirement
126
112
  requirements:
127
113
  - - "~>"
128
114
  - !ruby/object:Gem::Version
129
- version: '1.0'
115
+ version: '1.26'
130
116
  - !ruby/object:Gem::Dependency
131
117
  name: rubocop-rspec
132
118
  requirement: !ruby/object:Gem::Requirement
133
119
  requirements:
134
120
  - - "~>"
135
121
  - !ruby/object:Gem::Version
136
- version: '3.0'
122
+ version: '3.8'
137
123
  type: :development
138
124
  prerelease: false
139
125
  version_requirements: !ruby/object:Gem::Requirement
140
126
  requirements:
141
127
  - - "~>"
142
128
  - !ruby/object:Gem::Version
143
- version: '3.0'
129
+ version: '3.8'
144
130
  description: 'Faraday plugin to automatically set compression headers (GZip, Deflate,
145
131
  Brotli) and decompress the response.
146
132
 
@@ -163,7 +149,7 @@ licenses:
163
149
  metadata:
164
150
  bug_tracker_uri: https://github.com/bodrovis/faraday-gzip/issues
165
151
  changelog_uri: https://github.com/bodrovis/faraday-gzip/blob/master/CHANGELOG.md
166
- documentation_uri: http://www.rubydoc.info/gems/faraday-gzip/3.0.4
152
+ documentation_uri: http://www.rubydoc.info/gems/faraday-gzip/3.1.0
167
153
  source_code_uri: https://github.com/bodrovis/faraday-gzip
168
154
  rubygems_mfa_required: 'true'
169
155
  rdoc_options: []
@@ -180,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
166
  - !ruby/object:Gem::Version
181
167
  version: '0'
182
168
  requirements: []
183
- rubygems_version: 3.6.7
169
+ rubygems_version: 4.0.3
184
170
  specification_version: 4
185
171
  summary: Automatically sets compression headers and decompresses the response
186
172
  test_files: []