File: accept_encoding.rb

package info (click to toggle)
ruby-protocol-http 0.55.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 840 kB
  • sloc: ruby: 6,904; makefile: 4
file content (168 lines) | stat: -rw-r--r-- 6,005 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2025, by Samuel Williams.

require "protocol/http/accept_encoding"

describe Protocol::HTTP::AcceptEncoding do
	let(:delegate) do
		proc do |request|
			Protocol::HTTP::Response[200, Protocol::HTTP::Headers["content-type" => "text/plain"], ["Hello World!"]]
		end
	end
	
	let(:middleware) {Protocol::HTTP::AcceptEncoding.new(delegate)}
	
	with "known encodings" do
		it "can decode gzip responses" do
			# Mock a response with gzip encoding
			gzip_delegate = proc do |request|
				Protocol::HTTP::Response[200, 
					Protocol::HTTP::Headers[
						"content-type" => "text/plain",
						"content-encoding" => "gzip"
					], 
					["Hello World!"]
				]
			end
			
			gzip_middleware = Protocol::HTTP::AcceptEncoding.new(gzip_delegate)
			request = Protocol::HTTP::Request["GET", "/"]
			response = gzip_middleware.call(request)
			
			expect(response.headers).not.to have_keys("content-encoding")
			expect(response.body).to be_a(Protocol::HTTP::Body::Inflate)
		end
	end
	
	with "unknown encodings" do
		it "preserves unknown content-encoding headers" do
			# Mock a response with brotli encoding (not in DEFAULT_WRAPPERS)
			br_delegate = proc do |request|
				Protocol::HTTP::Response[200,
					Protocol::HTTP::Headers[
						"content-type" => "text/plain",
						"content-encoding" => "br"
					], 
					["Hello World!"]  # This would actually be brotli-encoded in reality
				]
			end
			
			br_middleware = Protocol::HTTP::AcceptEncoding.new(br_delegate)
			request = Protocol::HTTP::Request["GET", "/"]
			response = br_middleware.call(request)
			
			# The bug: this currently fails because content-encoding gets removed
			# when the middleware encounters an unknown encoding
			expect(response.headers).to have_keys("content-encoding")
			expect(response.headers["content-encoding"]).to be == ["br"]
			# The body should remain untouched since we can't decode it
			expect(response.body).not.to be_a(Protocol::HTTP::Body::Inflate)
		end
		
		it "preserves mixed known and unknown encodings" do
			# Mock a response with multiple encodings where some are unknown
			mixed_delegate = proc do |request|
				Protocol::HTTP::Response[200, 
					Protocol::HTTP::Headers[
						"content-type" => "text/plain",
						"content-encoding" => "gzip, br"  # gzip is known, br is unknown
					], 
					["Hello World!"]
				]
			end
			
			mixed_middleware = Protocol::HTTP::AcceptEncoding.new(mixed_delegate)
			request = Protocol::HTTP::Request["GET", "/"]
			response = mixed_middleware.call(request)
			
			# The bug: this currently fails because the entire content-encoding 
			# header gets removed when ANY unknown encoding is present
			expect(response.headers).to have_keys("content-encoding")
			expect(response.headers["content-encoding"]).to be == ["gzip", "br"]
			# The body should remain untouched since we can't decode the br part
			expect(response.body).not.to be_a(Protocol::HTTP::Body::Inflate)
		end
		
		it "handles case-insensitive encoding names" do
			# Mock a response with uppercase encoding name
			uppercase_delegate = proc do |request|
				Protocol::HTTP::Response[200, 
					Protocol::HTTP::Headers[
						"content-type" => "text/plain",
						"content-encoding" => "GZIP"
					], 
					["Hello World!"]
				]
			end
			
			uppercase_middleware = Protocol::HTTP::AcceptEncoding.new(uppercase_delegate)
			request = Protocol::HTTP::Request["GET", "/"]
			response = uppercase_middleware.call(request)
			
			# This might also be a bug - encoding names should be case-insensitive
			# but the current implementation uses exact string matching
			expect(response.headers).not.to have_keys("content-encoding")
			expect(response.body).to be_a(Protocol::HTTP::Body::Inflate)
		end
	end
	
	with "issue #86 - transparent proxy scenario" do
		it "preserves unknown content-encoding when acting as transparent proxy" do
			# This test simulates the exact scenario described in issue #86
			# where a transparent proxy fetches content with brotli encoding
			# but the AcceptEncoding middleware doesn't know about brotli
			
			# Mock upstream server that returns brotli-encoded content
			upstream_delegate = proc do |request|
				# Simulate a server responding with brotli encoding
				Protocol::HTTP::Response[200, 
					Protocol::HTTP::Headers[
						"content-type" => "text/html",
						"content-encoding" => "br"  # Server chose brotli
					], 
					["<compressed brotli content>"]  # This would be actual brotli data
				]
			end
			
			# Proxy middleware that only knows about gzip
			proxy_middleware = Protocol::HTTP::AcceptEncoding.new(upstream_delegate)
			
			# Client request that accepts both gzip and brotli
			request = Protocol::HTTP::Request["GET", "/some/resource"]
			response = proxy_middleware.call(request)
			
			# BUG: The content-encoding header should be preserved
			# so the client knows the content is still brotli-encoded
			expect(response.headers).to have_keys("content-encoding")
			expect(response.headers["content-encoding"]).to be == ["br"]
			
			# The body should remain untouched since proxy can't decode brotli
			expect(response.body).not.to be_a(Protocol::HTTP::Body::Inflate)
			expect(response.read).to be == "<compressed brotli content>"
		end
	end
	
	with "empty or identity encodings" do
		it "handles identity encoding correctly" do
			identity_delegate = proc do |request|
				Protocol::HTTP::Response[200,
					Protocol::HTTP::Headers[
						"content-type" => "text/plain",
						"content-encoding" => "identity"
					], 
					["Hello World!"]
				]
			end
			
			identity_middleware = Protocol::HTTP::AcceptEncoding.new(identity_delegate)
			request = Protocol::HTTP::Request["GET", "/"]
			response = identity_middleware.call(request)
			
			# Identity encoding means no encoding, so header should be removed
			expect(response.headers).not.to have_keys("content-encoding")
			expect(response.body).not.to be_a(Protocol::HTTP::Body::Inflate)
		end
	end
end