Skip to content

Commit 1210596

Browse files
bebacRX14
authored andcommittedJan 18, 2018
Add cache control headers to http static file handler + a few more mi… (#2470)
* Add cache control headers to http static file handler + a few more mime types * Remove Cache-Control header from static file handler * Undo extra mime types in static file handler * Fix code review issues: * use HTTP.rfc3339_date formatter * parse time value from If-Modified-Since header * compare header and mtime as older or equals * use `headers["If-Modified-Since"]?`
1 parent 3b50388 commit 1210596

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed
 

‎spec/std/http/server/handlers/static_file_handler_spec.cr

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
require "spec"
22
require "http/server"
33

4-
private def handle(request, fallthrough = true, directory_listing = true)
4+
private def handle(request, fallthrough = true, directory_listing = true, ignore_body = false)
55
io = IO::Memory.new
66
response = HTTP::Server::Response.new(io)
77
context = HTTP::Server::Context.new(request, response)
88
handler = HTTP::StaticFileHandler.new "#{__DIR__}/static", fallthrough, directory_listing
99
handler.call context
1010
response.close
1111
io.rewind
12-
HTTP::Client::Response.from_io(io)
12+
HTTP::Client::Response.from_io(io, ignore_body)
1313
end
1414

1515
describe HTTP::StaticFileHandler do
@@ -21,6 +21,33 @@ describe HTTP::StaticFileHandler do
2121
response.body.should eq(File.read("#{__DIR__}/static/test.txt"))
2222
end
2323

24+
context "with header If-Modified-Since" do
25+
it "should return 304 Not Modified if file mtime is equal" do
26+
headers = HTTP::Headers.new
27+
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime)
28+
response = handle HTTP::Request.new("GET", "/test.txt", headers), ignore_body: true
29+
response.status_code.should eq(304)
30+
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime))
31+
end
32+
33+
it "should return 304 Not Modified if file mtime is older" do
34+
headers = HTTP::Headers.new
35+
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime + 1.hour)
36+
response = handle HTTP::Request.new("GET", "/test.txt", headers), ignore_body: true
37+
response.status_code.should eq(304)
38+
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime))
39+
end
40+
41+
it "should serve file if file mtime is younger" do
42+
headers = HTTP::Headers.new
43+
headers["If-Modified-Since"] = HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime - 1.hour)
44+
response = handle HTTP::Request.new("GET", "/test.txt")
45+
response.status_code.should eq(200)
46+
response.headers["Last-Modified"].should eq(HTTP.rfc1123_date(File.stat("#{__DIR__}/static/test.txt").mtime))
47+
response.body.should eq(File.read("#{__DIR__}/static/test.txt"))
48+
end
49+
end
50+
2451
it "should list directory's entries" do
2552
response = handle HTTP::Request.new("GET", "/")
2653
response.status_code.should eq(200)

‎src/http/server/handlers/static_file_handler.cr

+17
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,23 @@ class HTTP::StaticFileHandler
6464
context.response.content_type = "text/html"
6565
directory_listing(context.response, request_path, file_path)
6666
elsif is_file
67+
last_modified = File.stat(file_path).mtime
68+
context.response.headers["Last-Modified"] = HTTP.rfc1123_date(last_modified)
69+
70+
if if_modified_since = context.request.headers["If-Modified-Since"]?
71+
# TODO: Use a more generalized time format parser for better compatibility to RFC 7232
72+
header_time = Time.parse(if_modified_since, "%a, %d %b %Y %H:%M:%S GMT")
73+
74+
# File mtime probably has a higher resolution than the header value.
75+
# An exact comparison might be slightly off, so we add 1s padding.
76+
# Static files should generally not be modified in subsecond intervals, so this is perfectly safe.
77+
# This might replaced by a more sophisticated time comparison when it becomes available.
78+
if last_modified <= header_time + 1.second
79+
context.response.status_code = 304
80+
return
81+
end
82+
end
83+
6784
context.response.content_type = mime_type(file_path)
6885
context.response.content_length = File.size(file_path)
6986
File.open(file_path) do |file|

0 commit comments

Comments
 (0)
Please sign in to comment.