Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
**Master**

- Handle conditional request ETag lists, wildcards and weak comparison. [Oskar Eichler](https://github.com/OskarEichler)

Get upgrade notes from Sprockets 3.x to 4.x at https://github.com/rails/sprockets/blob/master/UPGRADING.md

# 4.4.1
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -714,3 +714,5 @@ Please see the [CHANGELOG](https://github.com/rails/sprockets/tree/master/CHANGE

## License
Sprockets is released under the [MIT License](MIT-LICENSE).

Conditional asset requests support ETag lists, wildcards and weak `If-None-Match` comparison.
18 changes: 13 additions & 5 deletions lib/sprockets/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ def call(env)
if fingerprint
if_match = fingerprint
elsif env['HTTP_IF_MATCH']
if_match = env['HTTP_IF_MATCH'][/"(\w+)"$/, 1]
if_match = env['HTTP_IF_MATCH']
end

if env['HTTP_IF_NONE_MATCH']
if_none_match = env['HTTP_IF_NONE_MATCH'][/"(\w+)"$/, 1]
if_none_match = env['HTTP_IF_NONE_MATCH']
end

# Look up the asset.
Expand All @@ -88,9 +88,9 @@ def call(env)
status = :not_found
elsif fingerprint && asset.etag != fingerprint
status = :not_found
elsif if_match && asset.etag != if_match
elsif if_match && !etag_match?(if_match, asset.etag)
status = :precondition_failed
elsif if_none_match && asset.etag == if_none_match
elsif if_none_match && etag_match?(if_none_match, asset.etag, weak: true)
status = :not_modified
else
status = :ok
Expand All @@ -102,7 +102,7 @@ def call(env)
ok_response(asset, env)
when :not_modified
logger.info "#{msg} 304 Not Modified (#{time_elapsed.call}ms)"
not_modified_response(env, if_none_match)
not_modified_response(env, asset.etag)
when :not_found
logger.info "#{msg} 404 Not Found (#{time_elapsed.call}ms)"
not_found_response(env)
Expand All @@ -129,6 +129,14 @@ def call(env)
end

private
def etag_match?(header, etag, weak: false)
header.to_s.split(',').any? do |candidate|
candidate = candidate.strip
candidate = candidate.delete_prefix('W/') if weak
candidate == '*' || candidate == etag || candidate == %Q("#{etag}")
end
end

def forbidden_request?(path)
# Prevent access to files elsewhere on the file system
#
Expand Down