Class: Utopia::Static::LocalFile
- Inherits:
-
Object
- Object
- Utopia::Static::LocalFile
- Defined in:
- lib/utopia/static/local_file.rb
Overview
Represents a local static resource and constructs responses for it.
Constant Summary collapse
- CONTENT_LENGTH =
"content-length".freeze
- CONTENT_RANGE =
"content-range".freeze
Instance Attribute Summary collapse
-
#etag ⇒ Object
readonly
Returns the value of attribute etag.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
Instance Method Summary collapse
-
#byte_ranges(request) ⇒ Object
Resolve satisfiable byte ranges from the parsed range header.
-
#bytesize ⇒ Object
Measure the file's content length.
-
#initialize(path) ⇒ LocalFile
constructor
Initialize metadata for a local file.
-
#modified?(request) ⇒ Boolean
Check whether the file has changed since the request validators.
-
#mtime_date ⇒ Object
Format the file's modification time for an HTTP header.
-
#serve(request, response_headers) ⇒ Object
Serve.
Constructor Details
#initialize(path) ⇒ LocalFile
Initialize metadata for a local file.
22 23 24 25 26 27 28 29 |
# File 'lib/utopia/static/local_file.rb', line 22 def initialize(path) @path = path @stat = File.stat(@path) @mtime_date = @stat.mtime.httpdate fingerprint = Digest::SHA1.hexdigest("#{@stat.size}:#{@stat.mtime.to_i}:#{@stat.mtime.nsec}") @etag = %Q{W/"#{fingerprint}"} end |
Instance Attribute Details
#etag ⇒ Object (readonly)
Returns the value of attribute etag.
32 33 34 |
# File 'lib/utopia/static/local_file.rb', line 32 def etag @etag end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
31 32 33 |
# File 'lib/utopia/static/local_file.rb', line 31 def path @path end |
Instance Method Details
#byte_ranges(request) ⇒ Object
Resolve satisfiable byte ranges from the parsed range header.
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/utopia/static/local_file.rb', line 102 def byte_ranges(request) return nil unless request.method == Protocol::HTTP::Methods::GET range = request.headers["range"] return nil unless range&.bytes? if validator = request.headers["if-range"] return nil unless if_range?(validator) end return range.resolve(bytesize) end |
#bytesize ⇒ Object
Measure the file's content length.
42 43 44 |
# File 'lib/utopia/static/local_file.rb', line 42 def bytesize @stat.size end |
#modified?(request) ⇒ Boolean
Check whether the file has changed since the request validators.
49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/utopia/static/local_file.rb', line 49 def modified?(request) if = request.headers["if-none-match"] return !.weak_match?(@etag) end if modified_since = request.headers["if-modified-since"] return @stat.mtime.to_i > modified_since.to_time.to_i end return true end |
#mtime_date ⇒ Object
Format the file's modification time for an HTTP header.
36 37 38 |
# File 'lib/utopia/static/local_file.rb', line 36 def mtime_date @mtime_date end |
#serve(request, response_headers) ⇒ Object
Serve.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/utopia/static/local_file.rb', line 68 def serve(request, response_headers) ranges = byte_ranges(request) size = bytesize # puts "Requesting ranges: #{ranges.inspect} (#{size})" if ranges == nil or ranges.size != 1 # No ranges, or multiple ranges (which we don't support). # TODO: Support multiple byte-ranges, for now just send entire file: status = 200 response_headers[CONTENT_LENGTH] = size.to_s range = nil else # Partial content: range = ranges[0] partial_size = range.size status = 206 response_headers[CONTENT_LENGTH] = partial_size.to_s response_headers[CONTENT_RANGE] = "bytes #{range.min}-#{range.max}/#{size}" end if request.head? body = Protocol::HTTP::Body::Head.new(size) else body = Protocol::HTTP::Body::File.open(@path, range, size: size) end return Response[status, response_headers, body] end |