fix stylesheet cache to recover if file is on disk

This commit is contained in:
Sam 2015-05-22 11:21:16 +10:00
parent 17d843a0ad
commit 96dbeb8608
4 changed files with 50 additions and 11 deletions

View File

@ -4,43 +4,53 @@ class StylesheetsController < ApplicationController
def show def show
target,digest = params[:name].split("_") target,digest = params[:name].split("_")
digest_orig = digest
digest = "_" + digest if digest
cache_time = request.env["HTTP_IF_MODIFIED_SINCE"] cache_time = request.env["HTTP_IF_MODIFIED_SINCE"]
cache_time = Time.rfc2822(cache_time) rescue nil if cache_time cache_time = Time.rfc2822(cache_time) rescue nil if cache_time
query = StylesheetCache.where(target: target) query = StylesheetCache.where(target: target)
if digest if digest
query = query.where(digest: digest_orig) query = query.where(digest: digest)
else else
query = query.order('id desc') query = query.order('id desc')
end end
# Security note, safe due to route constraint
underscore_digest = digest ? "_" + digest : ""
location = "#{Rails.root}/#{DiscourseStylesheets::CACHE_PATH}/#{target}#{underscore_digest}.css"
stylesheet_time = query.pluck(:created_at).first stylesheet_time = query.pluck(:created_at).first
if !stylesheet_time if !stylesheet_time
return render nothing: true, status: 404 handle_missing_cache(location, target, digest)
end end
if cache_time && stylesheet_time && stylesheet_time <= cache_time if cache_time && stylesheet_time && stylesheet_time <= cache_time
return render nothing: true, status: 304 return render nothing: true, status: 304
end end
# Security note, safe due to route constraint
location = "#{Rails.root}/#{DiscourseStylesheets::CACHE_PATH}/#{target}#{digest}.css"
unless File.exist?(location) unless File.exist?(location)
if current = query.first if current = query.first
File.write(location, current.content) File.write(location, current.content)
else else
return render nothing: true, status: 404 raise Discourse::NotFound
end end
end end
response.headers['Last-Modified'] = stylesheet_time.httpdate response.headers['Last-Modified'] = stylesheet_time.httpdate if stylesheet_time
expires_in 1.year, public: true unless Rails.env == "development" expires_in 1.year, public: true unless Rails.env == "development"
send_file(location, disposition: :inline) send_file(location, disposition: :inline)
end end
protected
def handle_missing_cache(location, name, digest)
existing = File.read(location) rescue nil
if existing && digest
StylesheetCache.add(name, digest, existing)
end
end
end end

View File

@ -1,7 +1,7 @@
class StylesheetCache < ActiveRecord::Base class StylesheetCache < ActiveRecord::Base
self.table_name = 'stylesheet_cache' self.table_name = 'stylesheet_cache'
MAX_TO_KEEP = 10 MAX_TO_KEEP = 50
def self.add(target,digest,content) def self.add(target,digest,content)

View File

@ -118,10 +118,14 @@ class DiscourseStylesheets
end end
end end
def cache_fullpath def self.cache_fullpath
"#{Rails.root}/#{CACHE_PATH}" "#{Rails.root}/#{CACHE_PATH}"
end end
def cache_fullpath
self.class.cache_fullpath
end
def stylesheet_fullpath def stylesheet_fullpath
"#{cache_fullpath}/#{stylesheet_filename}" "#{cache_fullpath}/#{stylesheet_filename}"
end end

View File

@ -0,0 +1,25 @@
require 'spec_helper'
describe StylesheetsController do
it 'can survive cache miss' do
DiscourseStylesheets.cache.clear
DiscourseStylesheets.stylesheet_link_tag('desktop_rtl')
StylesheetCache.destroy_all
# digestless
get :show, name: 'desktop_rtl'
expect(response).to be_success
# tmp folder destruction and cached
`rm #{DiscourseStylesheets.cache_fullpath}/*`
get :show, name: 'desktop_rtl'
expect(response).to be_success
# there is an edge case which is ... disk and db cache is nuked, very unlikely to happen
end
end