rack-brotli 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5463dfd275a000348ae1f5e821e18aed467183c673d9bf70d7387809b436889d
4
- data.tar.gz: c17ef9a3540c58eb2249935b06a550f3c9af067fb2974e4a3fb240922857dbf3
3
+ metadata.gz: 0b74bec06cbe4c1bb630015f38590e272bffa0bf0ae35543625bff909872d920
4
+ data.tar.gz: a34a2637ad36992b49368ed62cfc1ffeb195588d2eec5fe04c542b624447e8ce
5
5
  SHA512:
6
- metadata.gz: 716e8cc8d476fdcbd622577278546aa7a551054be994110156e349e6f1140a99425135964fe05d405cf8d6fee46e3cb41565ffb3fc9aabc2436adedc32910c0c
7
- data.tar.gz: e4335332c6ca053c5a49afc4837007c48d1bad9b52e1c7ee79cdb392faa2997d2dca7e251e0148835c4df6cd0917607ac75fb4d7dd5a4ffdba790eab1692b1b2
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,80 +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
- buffer = ''
77
- @body.each { |part|
78
- buffer << part
79
- }
80
- 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
81
97
  ensure
82
- @writer = nil
98
+ br.finish
83
99
  end
84
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.
85
107
  def close
86
108
  @body.close if @body.respond_to?(:close)
87
109
  end
88
110
  end
89
-
111
+
90
112
  private
91
113
 
114
+ # Whether the body should be compressed.
92
115
  def should_deflate?(env, status, headers, body)
93
116
  # Skip compressing empty entity body responses and responses with
94
117
  # no-transform set.
95
- if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
96
- headers[Rack::CACHE_CONTROL].to_s =~ /\bno-transform\b/ ||
97
- (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/)
98
121
  return false
99
122
  end
100
123
 
@@ -104,6 +127,10 @@ module Rack::Brotli
104
127
  # Skip if @condition lambda is given and evaluates to false
105
128
  return false if @condition && !@condition.call(env, status, headers, body)
106
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
+
107
134
  true
108
135
  end
109
136
  end
@@ -2,7 +2,7 @@ module Rack
2
2
  module Brotli
3
3
  class Version
4
4
  def self.to_s
5
- '1.1.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.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Costa
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-14 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: []
@@ -117,7 +55,7 @@ homepage: http://github.com/marcotc/rack-brotli/
117
55
  licenses:
118
56
  - MIT
119
57
  metadata: {}
120
- post_install_message:
58
+ post_install_message:
121
59
  rdoc_options:
122
60
  - "--line-numbers"
123
61
  - "--inline-source"
@@ -138,8 +76,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
76
  - !ruby/object:Gem::Version
139
77
  version: '0'
140
78
  requirements: []
141
- rubygems_version: 3.0.3
142
- signing_key:
79
+ rubygems_version: 3.3.7
80
+ signing_key:
143
81
  specification_version: 2
144
82
  summary: Brotli compression for Rack responses
145
83
  test_files: []