From 72e1824323ad4d95aac85db6672537a3e4d6a370 Mon Sep 17 00:00:00 2001 From: Baylee Schmeisser Date: Tue, 26 Jan 2021 18:11:44 -0800 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=93=82=20Add=20option=20for=20`gzip?= =?UTF-8?q?=5Fcompression=5Flevel`=20for=20`Rack::Deflater`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `: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. --- CHANGELOG.md | 1 + lib/rack/deflater.rb | 15 ++++++++++++--- test/spec_deflater.rb | 25 +++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df1b3ac05..b65cf2e89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/rack/deflater.rb b/lib/rack/deflater.rb index e177fabb0..eb4287ebe 100644 --- a/lib/rack/deflater.rb +++ b/lib/rack/deflater.rb @@ -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) @@ -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 @@ -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, diff --git a/test/spec_deflater.rb b/test/spec_deflater.rb index ed9cffeca..ad260e66b 100644 --- a/test/spec_deflater.rb +++ b/test/spec_deflater.rb @@ -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 From 572fe6203e6a75375d6d2a67993e340561dc0a43 Mon Sep 17 00:00:00 2001 From: Baylee Schmeisser Date: Tue, 26 Jan 2021 19:05:41 -0800 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=A6=20Fixup=20github=20actions=20t?= =?UTF-8?q?o=20install=20latest=20for=20ubuntu=20packages.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/development.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index c13796d2c..58be9d262 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -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 @@ -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