Files
prism/test/test_middleware_compression.rb
T

165 lines
5.7 KiB
Ruby

require 'minitest/autorun'
require 'prism'
require 'support'
require 'zlib'
require 'stringio'
class MiddlewareCompressionTest < Minitest::Test
include TestSupport
LARGE_BODY = 'x' * 1000
def gunzip(bytes)
Zlib::GzipReader.new(StringIO.new(bytes)).read
end
def app_returning(status, headers, body)
Prism::Middleware::Compression.new(lambda { |_env| [status, headers, body] })
end
def test_compresses_a_large_buffered_body_when_client_accepts_gzip
app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY])
env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip'})
status, headers, body = app.call(env)
assert_equal 200, status
assert_equal 'gzip', headers['Content-Encoding']
assert_equal LARGE_BODY, gunzip(body.join)
assert_equal body.join.bytesize.to_s, headers['Content-Length']
end
def test_leaves_the_body_alone_when_the_client_sends_no_accept_encoding
app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY])
status, headers, body = app.call(env_for('GET', '/posts'))
assert_equal 200, status
assert_nil headers['Content-Encoding']
assert_equal LARGE_BODY, body.join
end
def test_respects_an_explicit_gzip_q0_declining_the_encoding
app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY])
env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip;q=0, *;q=1'})
_, headers, body = app.call(env)
assert_nil headers['Content-Encoding']
assert_equal LARGE_BODY, body.join
end
def test_a_wildcard_accept_encoding_is_honored
app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY])
env = env_for('GET', '/posts', headers: {'Accept-Encoding' => '*'})
_, headers, body = app.call(env)
assert_equal 'gzip', headers['Content-Encoding']
assert_equal LARGE_BODY, gunzip(body.join)
end
def test_skips_bodies_smaller_than_the_threshold
app = app_returning(200, {'Content-Type' => 'text/html'}, ['tiny'])
env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip'})
_, headers, body = app.call(env)
assert_nil headers['Content-Encoding']
assert_equal 'tiny', body.join
end
def test_skips_a_response_that_already_carries_a_content_encoding
headers = {'Content-Type' => 'text/html', 'Content-Encoding' => 'br'}
app = app_returning(200, headers, [LARGE_BODY])
env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip'})
_, out_headers, body = app.call(env)
assert_equal 'br', out_headers['Content-Encoding']
assert_equal LARGE_BODY, body.join
end
def test_skips_media_content_types
app = app_returning(200, {'Content-Type' => 'image/png'}, [LARGE_BODY])
env = env_for('GET', '/logo.png', headers: {'Accept-Encoding' => 'gzip'})
_, headers, body = app.call(env)
assert_nil headers['Content-Encoding']
assert_equal LARGE_BODY, body.join
end
def test_skips_a_partial_content_range_response
headers = {'Content-Type' => 'text/plain', 'Content-Range' => 'bytes 0-99/1000'}
app = app_returning(206, headers, [LARGE_BODY[0, 100]])
env = env_for('GET', '/file', headers: {'Accept-Encoding' => 'gzip'})
status, out_headers, = app.call(env)
assert_equal 206, status
assert_nil out_headers['Content-Encoding']
end
def test_skips_a_304_not_modified_response
app = app_returning(304, {}, [])
env = env_for('GET', '/file', headers: {'Accept-Encoding' => 'gzip'})
status, headers, = app.call(env)
assert_equal 304, status
assert_nil headers['Content-Encoding']
end
def test_skips_head_requests
app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY])
env = env_for('HEAD', '/posts', headers: {'Accept-Encoding' => 'gzip'})
_, headers, body = app.call(env)
assert_nil headers['Content-Encoding']
assert_equal LARGE_BODY, body.join
end
def test_appends_to_an_existing_vary_header_instead_of_replacing_it
app = app_returning(200, {'Content-Type' => 'text/html', 'Vary' => 'Accept'}, [LARGE_BODY])
env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip'})
_, headers, = app.call(env)
assert_equal 'Accept, Accept-Encoding', headers['Vary']
end
def test_streams_a_streaming_body_as_a_single_gzip_stream_chunk_by_chunk
written_before_producer_returns = nil
downstream = lambda do |_env|
body = Prism::StreamingBody.new do |writer|
writer.write("id,title\n")
writer.write("1,one\n")
writer.write("2,two\n")
end
[200, {'Content-Type' => 'text/csv'}, body]
end
app = Prism::Middleware::Compression.new(downstream)
env = env_for('GET', '/posts/export', headers: {'Accept-Encoding' => 'gzip'})
status, headers, body = app.call(env)
assert_equal 200, status
assert_equal 'gzip', headers['Content-Encoding']
assert_nil headers['Content-Length']
assert_kind_of Prism::StreamingBody, body
chunks = []
body.each { |chunk| chunks << chunk }
assert chunks.size > 1, 'expected more than one gzip chunk (one per producer write)'
assert_equal "id,title\n1,one\n2,two\n", gunzip(chunks.join)
end
def test_a_second_each_on_the_wrapped_streaming_body_reruns_the_producer_with_a_fresh_gzip_stream
downstream = lambda do |_env|
body = Prism::StreamingBody.new { |writer| writer.write('hello world') }
[200, {'Content-Type' => 'text/plain'}, body]
end
app = Prism::Middleware::Compression.new(downstream)
env = env_for('GET', '/x', headers: {'Accept-Encoding' => 'gzip'})
_, _, body = app.call(env)
first = +''
body.each { |chunk| first << chunk }
second = +''
body.each { |chunk| second << chunk }
assert_equal 'hello world', gunzip(first)
assert_equal 'hello world', gunzip(second)
end
end