mirror of
https://github.com/discourse/discourse.git
synced 2026-09-05 04:40:41 -05:00
DEV: Add optional s3_stale_while_revalidate and s3_max_age for uploads (#36298)
With this change, S3's `stale-while-revalidate` and `max-age` response directives can be controlled. By default, off for `stale-while-revalidate`, and both are hidden site settings.
This commit is contained in:
@@ -2212,6 +2212,16 @@ files:
|
||||
s3_access_key_id:
|
||||
default: ""
|
||||
secret: true
|
||||
s3_max_age:
|
||||
default: 31556952
|
||||
min: 60
|
||||
max: 31556952
|
||||
hidden: true
|
||||
s3_stale_while_revalidate:
|
||||
default: 31556952
|
||||
min: 60
|
||||
max: 31556952
|
||||
hidden: true
|
||||
s3_secret_access_key:
|
||||
default: ""
|
||||
secret: true
|
||||
|
||||
@@ -90,8 +90,14 @@ module FileStore
|
||||
# cache file locally when needed
|
||||
cache_file(file, File.basename(path)) if opts[:cache_locally]
|
||||
|
||||
cache_control = "max-age=#{SiteSetting.s3_max_age}, public, immutable"
|
||||
if SiteSetting.s3_stale_while_revalidate != SiteSetting.defaults[:s3_stale_while_revalidate]
|
||||
cache_control =
|
||||
"#{cache_control}, stale-while-revalidate=#{SiteSetting.s3_stale_while_revalidate}"
|
||||
end
|
||||
|
||||
options = {
|
||||
cache_control: "max-age=31556952, public, immutable",
|
||||
cache_control: cache_control,
|
||||
content_type:
|
||||
opts[:content_type].presence || MiniMime.lookup_by_filename(filename)&.content_type,
|
||||
}.merge(default_s3_options(secure: opts[:private]))
|
||||
|
||||
@@ -54,6 +54,97 @@ RSpec.describe FileStore::S3Store do
|
||||
expect(upload.etag).to eq(etag)
|
||||
end
|
||||
|
||||
it "adds `stale-while-revalidate` response directive when `s3_stale_while_revalidate` site setting is set" do
|
||||
SiteSetting.s3_stale_while_revalidate = 3600
|
||||
expected_cache_control = "max-age=31556952, public, immutable, stale-while-revalidate=3600"
|
||||
s3_helper.expects(:s3_bucket).returns(s3_bucket).at_least_once
|
||||
|
||||
s3_bucket
|
||||
.expects(:object)
|
||||
.with(regexp_matches(%r{original/\d+X.*/#{upload.sha1}\.png}))
|
||||
.returns(s3_object)
|
||||
|
||||
s3_object
|
||||
.expects(:put)
|
||||
.with(
|
||||
{
|
||||
acl: FileStore::S3Store::CANNED_ACL_PUBLIC_READ,
|
||||
cache_control: expected_cache_control,
|
||||
content_type: "image/png",
|
||||
content_disposition: "inline; filename=\"logo.png\"; filename*=UTF-8''logo.png",
|
||||
body: uploaded_file,
|
||||
},
|
||||
)
|
||||
.returns(Aws::S3::Types::PutObjectOutput.new(etag: "\"#{etag}\""))
|
||||
|
||||
expect(store.store_upload(uploaded_file, upload)).to match(
|
||||
%r{//s3-upload-bucket\.s3\.dualstack\.us-west-1\.amazonaws\.com/original/\d+X.*/#{upload.sha1}\.png},
|
||||
)
|
||||
|
||||
expect(upload.etag).to eq(etag)
|
||||
end
|
||||
|
||||
it "respects `s3_max_age` site setting for cache_control" do
|
||||
SiteSetting.s3_max_age = 60
|
||||
expected_cache_control = "max-age=60, public, immutable"
|
||||
s3_helper.expects(:s3_bucket).returns(s3_bucket).at_least_once
|
||||
|
||||
s3_bucket
|
||||
.expects(:object)
|
||||
.with(regexp_matches(%r{original/\d+X.*/#{upload.sha1}\.png}))
|
||||
.returns(s3_object)
|
||||
|
||||
s3_object
|
||||
.expects(:put)
|
||||
.with(
|
||||
{
|
||||
acl: FileStore::S3Store::CANNED_ACL_PUBLIC_READ,
|
||||
cache_control: expected_cache_control,
|
||||
content_type: "image/png",
|
||||
content_disposition: "inline; filename=\"logo.png\"; filename*=UTF-8''logo.png",
|
||||
body: uploaded_file,
|
||||
},
|
||||
)
|
||||
.returns(Aws::S3::Types::PutObjectOutput.new(etag: "\"#{etag}\""))
|
||||
|
||||
expect(store.store_upload(uploaded_file, upload)).to match(
|
||||
%r{//s3-upload-bucket\.s3\.dualstack\.us-west-1\.amazonaws\.com/original/\d+X.*/#{upload.sha1}\.png},
|
||||
)
|
||||
|
||||
expect(upload.etag).to eq(etag)
|
||||
end
|
||||
|
||||
describe "when default site settings are set" do
|
||||
it "cache_control is `max-age=31556952, public, immutable`" do
|
||||
default_cache_control = "max-age=31556952, public, immutable"
|
||||
|
||||
s3_helper.expects(:s3_bucket).returns(s3_bucket).at_least_once
|
||||
|
||||
s3_bucket
|
||||
.expects(:object)
|
||||
.with(regexp_matches(%r{original/\d+X.*/#{upload.sha1}\.png}))
|
||||
.returns(s3_object)
|
||||
|
||||
s3_object
|
||||
.expects(:put)
|
||||
.with(
|
||||
{
|
||||
acl: FileStore::S3Store::CANNED_ACL_PUBLIC_READ,
|
||||
cache_control: default_cache_control,
|
||||
content_type: "image/png",
|
||||
content_disposition: "inline; filename=\"logo.png\"; filename*=UTF-8''logo.png",
|
||||
body: uploaded_file,
|
||||
},
|
||||
)
|
||||
.returns(Aws::S3::Types::PutObjectOutput.new(etag: "\"#{etag}\""))
|
||||
|
||||
expect(store.store_upload(uploaded_file, upload)).to match(
|
||||
%r{//s3-upload-bucket\.s3\.dualstack\.us-west-1\.amazonaws\.com/original/\d+X.*/#{upload.sha1}\.png},
|
||||
)
|
||||
expect(upload.etag).to eq(etag)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when s3_upload_bucket includes folders path" do
|
||||
before do
|
||||
s3_object.stubs(:put).returns(Aws::S3::Types::PutObjectOutput.new(etag: "\"#{etag}\""))
|
||||
|
||||
Reference in New Issue
Block a user