static-rails 0.0.10 → 0.0.11

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: 7c20abb5a4b37f323a10e55d18ad7a65d0cb4219bf77bf1a9869d2ed0ab8f446
4
- data.tar.gz: 3ca5e505c0b05eb36846153d0cb6b05947f3359eb1a92c6cfb477d99fc368484
3
+ metadata.gz: d5808adb0f3f344a5706a6dddb5053899047f5d8aa4bbddb577168319b9aa046
4
+ data.tar.gz: d84c08d618e80a28b95be9da396f34d57636985ecf83859a906c5997a1532238
5
5
  SHA512:
6
- metadata.gz: eb489b95dfc77757c3e72e3612c7d5fa021393684b230de937355e230778d371468922e1d5277d3d0ef819b106d5ab07db09dc7284b9a6eefe1098baec9dc58e
7
- data.tar.gz: cbba26724c30fcfec192893973d5ec83de0d50ab719b05c842e457d504319d4658b65688f154566091e8a3bd50c27e8cc7df5bd5ac1645e73b67a821e866150a
6
+ metadata.gz: adb43e14b69561af8ce12989d319782b95d8c915b30968c4ce28d6bbf109426b8d606ba83d860002c2d793c16be3fbfe6c962be473df83507fd18d1e16de66c4
7
+ data.tar.gz: 7f4e6b9a9ed8dabaccd3a0da66671c36605a56e15da8483fe17f9518badbe3be6e2b82369ac4e7a05af9b4d594eaec1b010ad7616fc77b3aa835f81e0f87aa57
@@ -1,3 +1,10 @@
1
+ ## 0.0.11
2
+
3
+ * Inline the `ActionDispatch::FileHandler` from Rails master so that we can
4
+ target a single stable version of its API and control what MIME types it
5
+ considers to be compressible (bonus is that it effectively backports brotli
6
+ compression to pre-6.1 rails apps)
7
+
1
8
  ## 0.0.10
2
9
 
3
10
  * Change default `cache-control` header for static assets being served from disk
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- static-rails (0.0.10)
4
+ static-rails (0.0.11)
5
5
  rack-proxy (~> 0.6)
6
6
  railties (>= 5.0.0)
7
7
 
@@ -0,0 +1,173 @@
1
+ module StaticRails
2
+ # This class was extracted from Ruby on Rails:
3
+ #
4
+ # - actionpack/lib/action_dispatch/middleware/static.rb
5
+ #
6
+ # Copyright (c) 2005-2020 David Heinemeier Hansson, Ryan Edward Hall, Jeremy Daer
7
+ #
8
+ # License here: https://github.com/rails/rails/blob/master/MIT-LICENSE
9
+ #
10
+ # This endpoint serves static files from disk using Rack::File.
11
+ #
12
+ # URL paths are matched with static files according to expected
13
+ # conventions: +path+, +path+.html, +path+/index.html.
14
+ #
15
+ # Precompressed versions of these files are checked first. Brotli (.br)
16
+ # and gzip (.gz) files are supported. If +path+.br exists, this
17
+ # endpoint returns that file with a +Content-Encoding: br+ header.
18
+ #
19
+ # If no matching file is found, this endpoint responds 404 Not Found.
20
+ #
21
+ # Pass the +root+ directory to search for matching files, an optional
22
+ # +index: "index"+ to change the default +path+/index.html, and optional
23
+ # additional response headers.
24
+ class FileHandler
25
+ # Accept-Encoding value -> file extension
26
+ PRECOMPRESSED = {
27
+ "br" => ".br",
28
+ "gzip" => ".gz",
29
+ "identity" => nil
30
+ }
31
+
32
+ def initialize(root, index: "index", headers: {}, precompressed: %i[br gzip], compressible_content_types: /\A(?:text\/|application\/javascript)/)
33
+ @root = root.chomp("/").b
34
+ @index = index
35
+
36
+ @precompressed = Array(precompressed).map(&:to_s) | %w[identity]
37
+ @compressible_content_types = compressible_content_types
38
+
39
+ @file_server = ::Rack::File.new(@root, headers)
40
+ end
41
+
42
+ def call(env)
43
+ attempt(env) || @file_server.call(env)
44
+ end
45
+
46
+ def attempt(env)
47
+ request = Rack::Request.new env
48
+
49
+ if request.get? || request.head?
50
+ if (found = find_file(request.path_info, accept_encoding: request.accept_encoding))
51
+ serve request, *found
52
+ end
53
+ end
54
+ end
55
+
56
+ def serve(request, filepath, content_headers)
57
+ original, request.path_info =
58
+ request.path_info, ::Rack::Utils.escape_path(filepath).b
59
+
60
+ @file_server.call(request.env).tap do |status, headers, body|
61
+ # Omit Content-Encoding/Type/etc headers for 304 Not Modified
62
+ if status != 304
63
+ headers.update(content_headers)
64
+ end
65
+ end
66
+ ensure
67
+ request.path_info = original
68
+ end
69
+
70
+ # Match a URI path to a static file to be served.
71
+ #
72
+ # Used by the +Static+ class to negotiate a servable file in the
73
+ # +public/+ directory (see Static#call).
74
+ #
75
+ # Checks for +path+, +path+.html, and +path+/index.html files,
76
+ # in that order, including .br and .gzip compressed extensions.
77
+ #
78
+ # If a matching file is found, the path and necessary response headers
79
+ # (Content-Type, Content-Encoding) are returned.
80
+ def find_file(path_info, accept_encoding:)
81
+ each_candidate_filepath(path_info) do |filepath, content_type|
82
+ if (response = try_files(filepath, content_type, accept_encoding: accept_encoding))
83
+ return response
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def try_files(filepath, content_type, accept_encoding:)
91
+ headers = {"Content-Type" => content_type}
92
+
93
+ if compressible? content_type
94
+ try_precompressed_files filepath, headers, accept_encoding: accept_encoding
95
+ elsif file_readable? filepath
96
+ [filepath, headers]
97
+ end
98
+ end
99
+
100
+ def try_precompressed_files(filepath, headers, accept_encoding:)
101
+ each_precompressed_filepath(filepath) do |content_encoding, precompressed_filepath|
102
+ if file_readable? precompressed_filepath
103
+ # Identity encoding is default, so we skip Accept-Encoding
104
+ # negotiation and needn't set Content-Encoding.
105
+ #
106
+ # Vary header is expected when we've found other available
107
+ # encodings that Accept-Encoding ruled out.
108
+ if content_encoding == "identity"
109
+ return precompressed_filepath, headers
110
+ else
111
+ headers["Vary"] = "Accept-Encoding"
112
+
113
+ if accept_encoding.any? { |enc, _| /\b#{content_encoding}\b/i.match?(enc) }
114
+ headers["Content-Encoding"] = content_encoding
115
+ return precompressed_filepath, headers
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ def file_readable?(path)
123
+ file_stat = File.stat(File.join(@root, path.b))
124
+ rescue SystemCallError
125
+ false
126
+ else
127
+ file_stat.file? && file_stat.readable?
128
+ end
129
+
130
+ def compressible?(content_type)
131
+ @compressible_content_types.match?(content_type)
132
+ end
133
+
134
+ def each_precompressed_filepath(filepath)
135
+ @precompressed.each do |content_encoding|
136
+ precompressed_ext = PRECOMPRESSED.fetch(content_encoding)
137
+ yield content_encoding, "#{filepath}#{precompressed_ext}"
138
+ end
139
+
140
+ nil
141
+ end
142
+
143
+ def each_candidate_filepath(path_info)
144
+ return unless (path = clean_path(path_info))
145
+
146
+ ext = ::File.extname(path)
147
+ content_type = ::Rack::Mime.mime_type(ext, nil)
148
+ yield path, content_type || "text/plain"
149
+
150
+ # Tack on .html and /index.html only for paths that don't have
151
+ # an explicit, resolvable file extension. No need to check
152
+ # for foo.js.html and foo.js/index.html.
153
+ unless content_type
154
+ default_ext = ::ActionController::Base.default_static_extension
155
+ if ext != default_ext
156
+ default_content_type = ::Rack::Mime.mime_type(default_ext, "text/plain")
157
+
158
+ yield "#{path}#{default_ext}", default_content_type
159
+ yield "#{path}/#{@index}#{default_ext}", default_content_type
160
+ end
161
+ end
162
+
163
+ nil
164
+ end
165
+
166
+ def clean_path(path_info)
167
+ path = ::Rack::Utils.unescape_path path_info.chomp("/")
168
+ if ::Rack::Utils.valid_path? path
169
+ ::Rack::Utils.clean_path_info path
170
+ end
171
+ end
172
+ end
173
+ end
@@ -1,6 +1,6 @@
1
1
  require "rack-proxy"
2
- require "action_dispatch/middleware/static"
3
2
 
3
+ require_relative "file_handler"
4
4
  require_relative "matches_request_to_static_site"
5
5
 
6
6
  module StaticRails
@@ -18,9 +18,8 @@ module StaticRails
18
18
  if (req.get? || req.head?) && (site = @matches_request_to_static_site.call(req))
19
19
  file_handler = file_handler_for(site)
20
20
  path = req.path_info.gsub(/^#{site.url_root_path}/, "").chomp("/")
21
- if (match = matching_file_for(file_handler, site, path))
22
- req.path_info = match
23
- return file_handler.serve(req)
21
+ if (found = find_file_for(file_handler, site, path, req.accept_encoding))
22
+ return file_handler.serve(req, *found)
24
23
  end
25
24
  end
26
25
 
@@ -32,19 +31,20 @@ module StaticRails
32
31
  # The same file handler used by Rails when serving up files from /public
33
32
  # See: actionpack/lib/action_dispatch/middleware/static.rb
34
33
  def file_handler_for(site)
35
- @file_handlers[site] ||= ActionDispatch::FileHandler.new(
34
+ @file_handlers[site] ||= FileHandler.new(
36
35
  StaticRails.config.app.root.join(site.compile_dir).to_s,
37
36
  headers: {
38
37
  "cache-control" => "public; max-age=31536000"
39
- }
38
+ },
39
+ compressible_content_types: /^text\/|[\/+](javascript|json|text|xml|css|yaml)$/i
40
40
  )
41
41
  end
42
42
 
43
- def matching_file_for(file_handler, site, path)
44
- if (match = file_handler.match?(path))
45
- match
43
+ def find_file_for(file_handler, site, path, accept_encoding)
44
+ if (found = file_handler.find_file(path, accept_encoding: accept_encoding))
45
+ found
46
46
  elsif site.compile_404_file_path.present?
47
- file_handler.match?(site.compile_404_file_path)
47
+ file_handler.find_file(site.compile_404_file_path, accept_encoding: accept_encoding)
48
48
  end
49
49
  end
50
50
  end
@@ -1,3 +1,3 @@
1
1
  module StaticRails
2
- VERSION = "0.0.10"
2
+ VERSION = "0.0.11"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: static-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
@@ -64,6 +64,7 @@ files:
64
64
  - lib/static-rails/configuration.rb
65
65
  - lib/static-rails/determines_whether_to_handle_request.rb
66
66
  - lib/static-rails/error.rb
67
+ - lib/static-rails/file_handler.rb
67
68
  - lib/static-rails/gets_csrf_token.rb
68
69
  - lib/static-rails/matches_request_to_static_site.rb
69
70
  - lib/static-rails/proxy_middleware.rb