rack-brotli 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9664462a848cd456a905f8443b7da55ccd77cb195ab4de07c906565eda3cc233
4
- data.tar.gz: 8eb1ef7457774d7df327f67b571abe0f4416497c1d1c27dbfabe31d79eb7dc41
3
+ metadata.gz: 0b74bec06cbe4c1bb630015f38590e272bffa0bf0ae35543625bff909872d920
4
+ data.tar.gz: a34a2637ad36992b49368ed62cfc1ffeb195588d2eec5fe04c542b624447e8ce
5
5
  SHA512:
6
- metadata.gz: 75b8954ad9aac90190159ccbe97f2e25952d4c28f7ce408a49c1f5e013dff1db62cb051221c3a4c84a278626a71a7e1faea641da68ea13632e62ecc42f4a60e0
7
- data.tar.gz: b6263aa03b94babf5490f40345e32a379594a54ce5749463b5d4a57140ef8476fe3bd0e03e8a3ebc90cfc0145b8cb22d915d65956d2b7f9260bd1284fe8ff977
6
+ metadata.gz: 677a662e25d59d451e7fd8477fa135febf135df5e9764282c2340df9c848a4c2e3579a7b7cb6de0ddde4cbeb2bda3478ff3f43377fc888db202f1bb6172f3c47
7
+ data.tar.gz: f5c9643048b9002d0aad3b35e76c6b783c46306c26ab9e34cac5a6a3b25c1d7a0793793303acdfbb474a6dbd96fe2d881eb9eee30cd96d72f218aa74a01e4bb9
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Rack::Brotli [![Gem Version](https://badge.fury.io/rb/rack-brotli.svg)](https://badge.fury.io/rb/rack-brotli) [![Build Status](https://travis-ci.org/marcotc/rack-brotli.svg?branch=master)](https://travis-ci.org/marcotc/rack-brotli)
1
+ # Rack::Brotli [![Gem Version](https://badge.fury.io/rb/rack-brotli.svg)](https://badge.fury.io/rb/rack-brotli) [![Build Status](https://github.com/marcotc/rack-brotli/actions/workflows/test.yml/badge.svg)](https://github.com/marcotc/rack-brotli/actions/workflows/test.yml)
2
2
 
3
3
  `Rack::Brotli` compresses `Rack` responses using [Google's Brotli](https://github.com/google/brotli) compression algorithm.
4
4
 
@@ -17,7 +17,10 @@ Requiring `'rack/brotli'` will autoload `Rack::Brotli` module. The following exa
17
17
  require 'rack'
18
18
  require 'rack/brotli'
19
19
 
20
- use Rack::Brotli
20
+ use Rack::Brotli # Default compression quality is 5
21
+
22
+ # You can also provide native Brotli compression options:
23
+ # use Rack::Brotli, quality: 11
21
24
 
22
25
  run theapp
23
26
  ```
@@ -26,10 +29,10 @@ run theapp
26
29
 
27
30
  To run the entire test suite, run
28
31
 
29
- rake test
32
+ bundle exec rake test
30
33
 
31
34
  ### Links
32
35
 
33
- * rack-brotli on GitHub:: <http://github.com/marcotc/rack-brotli>
34
- * Rack:: <http://rack.rubyforge.org/>
35
- * Rack On GitHub:: <http://github.com/rack/rack>
36
+ * rack-brotli: <http://github.com/marcotc/rack-brotli>
37
+ * Brotli for Ruby: <https://github.com/miyucy/brotli>
38
+ * Rack: <http://github.com/rack/rack>
@@ -1,17 +1,13 @@
1
- require "brotli"
2
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
+
3
+ require 'brotli'
4
+ require "rack/utils"
5
+ require 'rack/request'
6
+ require 'rack/body_proxy'
3
7
 
4
8
  module Rack::Brotli
5
- # This middleware enables compression of http responses.
6
- #
7
- # Currently supported compression algorithms:
8
- #
9
- # * br
10
- #
11
- # The middleware automatically detects when compression is supported
12
- # and allowed. For example no transformation is made when a cache
13
- # directive of 'no-transform' is present, or when the response status
14
- # code is one that doesn't allow an entity body.
9
+ # This middleware enables compression of http responses with the `br` encoding,
10
+ # when support is detected and allowed.
15
11
  class Deflater
16
12
  ##
17
13
  # Creates Rack::Brotli middleware.
@@ -21,81 +17,107 @@ module Rack::Brotli
21
17
  # 'if' - a lambda enabling / disabling deflation based on returned boolean value
22
18
  # e.g use Rack::Brotli, :if => lambda { |env, status, headers, body| body.map(&:bytesize).reduce(0, :+) > 512 }
23
19
  # 'include' - a list of content types that should be compressed
24
- # 'deflater' - Brotli compression options
20
+ # 'deflater' - Brotli compression options Hash (see https://brotli.org/encode.html#a4d4 and https://github.com/miyucy/brotli/blob/ea0e058031177e5cc42e361f7d2702a951048a31/ext/brotli/brotli.c#L119-L180)
21
+ # - 'mode'
22
+ # - 'quality'
23
+ # - 'lgwin'
24
+ # - 'lgblock'
25
25
  def initialize(app, options = {})
26
26
  @app = app
27
27
 
28
28
  @condition = options[:if]
29
29
  @compressible_types = options[:include]
30
- @deflater_options = { quality: 5 }.merge(options[:deflater] || {})
30
+ @deflater_options = { quality: 5 }.merge(options.fetch(:deflater, {}))
31
+ @sync = options.fetch(:sync, true)
31
32
  end
32
33
 
33
34
  def call(env)
34
- status, headers, body = @app.call(env)
35
- headers = Rack::Utils::HeaderHash.new(headers)
35
+ status, headers, body = response = @app.call(env)
36
36
 
37
37
  unless should_deflate?(env, status, headers, body)
38
- return [status, headers, body]
38
+ return response
39
39
  end
40
40
 
41
41
  request = Rack::Request.new(env)
42
42
 
43
- encoding = Rack::Utils.select_best_encoding(%w(br),
44
- request.accept_encoding)
45
-
46
- return [status, headers, body] unless encoding
43
+ encoding = Rack::Utils.select_best_encoding(%w(br identity), request.accept_encoding)
47
44
 
48
45
  # Set the Vary HTTP header.
49
- vary = headers["Vary"].to_s.split(",").map(&:strip)
50
- unless vary.include?("*") || vary.include?("Accept-Encoding")
51
- headers["Vary"] = vary.push("Accept-Encoding").join(",")
46
+ vary = headers["vary"].to_s.split(",").map(&:strip)
47
+ unless vary.include?("*") || vary.any?{|v| v.downcase == 'accept-encoding'}
48
+ headers["vary"] = vary.push("Accept-Encoding").join(",")
52
49
  end
53
50
 
54
51
  case encoding
55
52
  when "br"
56
- headers['Content-Encoding'] = "br"
53
+ headers['content-encoding'] = "br"
57
54
  headers.delete(Rack::CONTENT_LENGTH)
58
- [status, headers, BrotliStream.new(body, @deflater_options)]
59
- when nil
55
+ response[2] = BrotliStream.new(body, @sync, @deflater_options)
56
+ response
57
+ when "identity"
58
+ response
59
+ else
60
+ # Only possible encoding values here are 'br', 'identity', and nil
60
61
  message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
61
62
  bp = Rack::BodyProxy.new([message]) { body.close if body.respond_to?(:close) }
62
- [406, {Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => message.length.to_s}, bp]
63
+ [406, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => message.length.to_s }, bp]
63
64
  end
64
65
  end
65
66
 
67
+ # Body class used for encoded responses.
66
68
  class BrotliStream
67
- include Rack::Utils
68
69
 
69
- def initialize(body, options)
70
+ BUFFER_LENGTH = 128 * 1_024
71
+
72
+ def initialize(body, sync, br_options)
70
73
  @body = body
71
- @options = options
74
+ @br_options = br_options
75
+ @sync = sync
72
76
  end
73
77
 
78
+ # Yield compressed strings to the given block.
74
79
  def each(&block)
75
80
  @writer = block
76
- # Use String.new instead of '' to support environments with strings frozen by default.
77
- buffer = String.new
78
- @body.each { |part|
79
- buffer << part
80
- }
81
- yield ::Brotli.deflate(buffer, @options)
81
+ br = Brotli::Writer.new(self, @br_options)
82
+ # @body.each is equivalent to @body.gets (slow)
83
+ if @body.is_a? ::File # XXX: Should probably be ::IO
84
+ while part = @body.read(BUFFER_LENGTH)
85
+ br.write(part)
86
+ br.flush if @sync
87
+ end
88
+ else
89
+ @body.each { |part|
90
+ # Skip empty strings, as they would result in no output,
91
+ # and flushing empty parts could raise an IO error.
92
+ next if part.empty?
93
+ br.write(part)
94
+ br.flush if @sync
95
+ }
96
+ end
82
97
  ensure
83
- @writer = nil
98
+ br.finish
84
99
  end
85
100
 
101
+ # Call the block passed to #each with the compressed data.
102
+ def write(data)
103
+ @writer.call(data)
104
+ end
105
+
106
+ # Close the original body if possible.
86
107
  def close
87
108
  @body.close if @body.respond_to?(:close)
88
109
  end
89
110
  end
90
-
111
+
91
112
  private
92
113
 
114
+ # Whether the body should be compressed.
93
115
  def should_deflate?(env, status, headers, body)
94
116
  # Skip compressing empty entity body responses and responses with
95
117
  # no-transform set.
96
- if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
97
- headers[Rack::CACHE_CONTROL].to_s =~ /\bno-transform\b/ ||
98
- (headers['Content-Encoding'] && headers['Content-Encoding'] !~ /\bidentity\b/)
118
+ if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) ||
119
+ /\bno-transform\b/.match?(headers[Rack::CACHE_CONTROL].to_s) ||
120
+ headers['content-encoding']&.!~(/\bidentity\b/)
99
121
  return false
100
122
  end
101
123
 
@@ -105,6 +127,10 @@ module Rack::Brotli
105
127
  # Skip if @condition lambda is given and evaluates to false
106
128
  return false if @condition && !@condition.call(env, status, headers, body)
107
129
 
130
+ # No point in compressing empty body, also handles usage with
131
+ # Rack::Sendfile.
132
+ return false if headers[Rack::CONTENT_LENGTH] == '0'
133
+
108
134
  true
109
135
  end
110
136
  end
@@ -2,7 +2,7 @@ module Rack
2
2
  module Brotli
3
3
  class Version
4
4
  def self.to_s
5
- '1.2.0'
5
+ '2.0.0'
6
6
  end
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-brotli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Costa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-13 00:00:00.000000000 Z
11
+ date: 2024-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -16,90 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.4'
19
+ version: '3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.4'
26
+ version: '3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: brotli
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.1.7
33
+ version: '0.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.1.7
41
- - !ruby/object:Gem::Dependency
42
- name: bundler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: minitest
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '5.6'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '5.6'
69
- - !ruby/object:Gem::Dependency
70
- name: rake
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '12'
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- version: 12.3.3
79
- type: :development
80
- prerelease: false
81
- version_requirements: !ruby/object:Gem::Requirement
82
- requirements:
83
- - - "~>"
84
- - !ruby/object:Gem::Version
85
- version: '12'
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: 12.3.3
89
- - !ruby/object:Gem::Dependency
90
- name: rdoc
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '3.12'
96
- type: :development
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: '3.12'
40
+ version: '0.3'
103
41
  description: Rack::Brotli enables Google's Brotli compression on HTTP responses
104
42
  email: marco@marcotc.com
105
43
  executables: []
@@ -138,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
76
  - !ruby/object:Gem::Version
139
77
  version: '0'
140
78
  requirements: []
141
- rubygems_version: 3.2.27
79
+ rubygems_version: 3.3.7
142
80
  signing_key:
143
81
  specification_version: 2
144
82
  summary: Brotli compression for Rack responses