Skip to content

Commit

Permalink
Make AssetServer threadsafe
Browse files Browse the repository at this point in the history
  • Loading branch information
josh committed Dec 9, 2009
1 parent 3dd120c commit 23815d7
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 22 deletions.
63 changes: 42 additions & 21 deletions lib/asset_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,60 +40,81 @@ def last_modified

def initialize(*paths)
@sources = paths.map { |path| Dir[path] }.flatten
@lock = Mutex.new
end

def call(env)
return not_found_response if @sources.empty?

rebundle if source_changed?
asset = rebundle(env)

if not_modified?(env) || etag_match?(env)
not_modified_response(env)
if not_modified?(asset, env) || etag_match?(asset, env)
not_modified_response(asset, env)
else
ok_response(env)
ok_response(asset, env)
end
end

private
def source_changed?
@asset.nil? || @asset.stale?
def source_changed?(asset)
asset.nil? || asset.stale?
end

def rebundle
@asset = Asset.new(@sources)
def rebundle(env)
if env["rack.multithread"]
synchronized_rebundle
else
rebundle!
end
end

def synchronized_rebundle
asset = @asset
if source_changed?(asset)
@lock.synchronize { rebundle! }
else
asset
end
end

def rebundle!
if source_changed?(@asset)
@asset = Asset.new(@sources)
end
@asset
end


def not_found_response
[ 404, { "Content-Type" => "text/plain", "Content-Length" => "9" }, [ "Not found" ] ]
end

def not_modified?(env)
env["HTTP_IF_MODIFIED_SINCE"] == @asset.created_at.httpdate
def not_modified?(asset, env)
env["HTTP_IF_MODIFIED_SINCE"] == asset.created_at.httpdate
end

def etag_match?(env)
env["HTTP_IF_NONE_MATCH"] == @asset.etag
def etag_match?(asset, env)
env["HTTP_IF_NONE_MATCH"] == asset.etag
end

def not_modified_response(env)
[ 304, headers(env), [] ]
def not_modified_response(asset, env)
[ 304, headers(asset, env), [] ]
end

def ok_response(env)
[ 200, headers(env), [@asset.source] ]
def ok_response(asset, env)
[ 200, headers(asset, env), [asset.source] ]
end

def headers(env)
def headers(asset, env)
Hash.new.tap do |headers|
headers["Content-Type"] = "text/javascript"
headers["Content-Length"] = @asset.source.size.to_s
headers["Content-Length"] = asset.source.size.to_s

headers["Cache-Control"] = "public, must-revalidate"
headers["Last-Modified"] = @asset.created_at.httpdate
headers["ETag"] = @asset.etag
headers["Last-Modified"] = asset.created_at.httpdate
headers["ETag"] = asset.etag

if env["QUERY_STRING"] == @asset.md5
if env["QUERY_STRING"] == asset.md5
headers["Cache-Control"] << ", max-age=#{1.year.to_i}"
end
end
Expand Down
2 changes: 1 addition & 1 deletion test/asset_server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def test_not_modified_response_when_headers_match

def test_if_sources_didnt_change_the_server_shouldnt_rebundle
Rack::MockRequest.new(App).get("/javascripts/all.js")
Assets::BundleServer.any_instance.expects(:rebundle).never
Assets::BundleServer::Asset.any_instance.expects(:new).never
Rack::MockRequest.new(App).get("/javascripts/all.js")
end

Expand Down

0 comments on commit 23815d7

Please sign in to comment.