Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

馃搨 Add option for gzip_compression_level for Rack::Deflater. #1734

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/development.yml
Expand Up @@ -24,6 +24,10 @@ jobs:
restore-keys: |
bundle-use-ruby-${{matrix.os}}-${{matrix.ruby}}-

- name: Updating packages (ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update

- name: Installing packages (ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install libfcgi-dev libmemcached-dev
Expand Down Expand Up @@ -61,6 +65,10 @@ jobs:
restore-keys: |
bundle-use-ruby-${{matrix.os}}-${{matrix.ruby}}-

- name: Updating packages (ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update

- name: Installing packages (ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install libmemcached-dev
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. For info on
### Added

- `Rack::RewindableInput` supports size. ([@ahorek](https://github.com/ahorek))
- `Rack::Deflater` supports `gzip_compression_level`. ([@bayleedev](https://github.com/bayleedev))

### Changed

Expand Down
15 changes: 12 additions & 3 deletions lib/rack/deflater.rb
Expand Up @@ -33,11 +33,18 @@ class Deflater
# :sync :: determines if the stream is going to be flushed after every chunk. Flushing after every chunk reduces
# latency for time-sensitive streaming applications, but hurts compression and throughput.
# Defaults to +true+.
# :gzip_compression_level :: An integer from 0 to 9 controlling the level of
# compression; 1 is fastest and produces the least
# compression, and 9 is slowest and produces the
# most compression. 0 is no compression. The
# default is 6. This is only needed for the `gzip`
# type encoding.
def initialize(app, options = {})
@app = app
@condition = options[:if]
@compressible_types = options[:include]
@sync = options.fetch(:sync, true)
@gzip_compression_level = options.fetch(:gzip_compression_level, 6)
end

def call(env)
Expand Down Expand Up @@ -65,7 +72,7 @@ def call(env)
headers.delete(CONTENT_LENGTH)
mtime = headers["Last-Modified"]
mtime = Time.httpdate(mtime).to_i if mtime
[status, headers, GzipStream.new(body, mtime, @sync)]
[status, headers, GzipStream.new(body, mtime, @sync, @gzip_compression_level)]
when "identity"
[status, headers, body]
when nil
Expand All @@ -82,16 +89,18 @@ class GzipStream
# mtime :: The modification time of the body, used to set the
# modification time in the gzip header.
# sync :: Whether to flush each gzip chunk as soon as it is ready.
def initialize(body, mtime, sync)
# gzip_compression_level :: The compression value for gzip.
def initialize(body, mtime, sync, gzip_compression_level)
@body = body
@mtime = mtime
@sync = sync
@gzip_compression_level = gzip_compression_level
end

# Yield gzip compressed strings to the given block.
def each(&block)
@writer = block
gzip = ::Zlib::GzipWriter.new(self)
gzip = ::Zlib::GzipWriter.new(self, @gzip_compression_level)
gzip.mtime = @mtime if @mtime
@body.each { |part|
# Skip empty strings, as they would result in no output,
Expand Down
25 changes: 25 additions & 0 deletions test/spec_deflater.rb
Expand Up @@ -402,6 +402,31 @@ class << app_body; def each; yield('foo'); yield('bar'); end; end
verify(200, response, 'gzip', options)
end

it 'will change gzip level' do
response = 'Hello World!' * 1000
small_options = {
'deflater_options' => { gzip_compression_level: 9 },
'skip_body_verify' => true,
}
fast_options = {
'deflater_options' => { gzip_compression_level: 1 },
'skip_body_verify' => true,
}
verify(200, response, 'gzip', small_options) do |_, _, body_small|
verify(200, response, 'gzip', fast_options) do |_, _, body_fast|
small_bytes = 0
body_small.each do |part|
small_bytes += part.bytesize
end
fast_bytes = 0
body_fast.each do |part|
fast_bytes += part.bytesize
end
assert small_bytes < fast_bytes
end
end
end

it 'will honor sync: false to avoid unnecessary flushing' do
app_body = Object.new
class << app_body
Expand Down