class Rack::TailFile
Rack::File serves files below the root
directory given, according to the path info of the Rack
request. e.g. when Rack::File.new(“/etc”) is used, you can access 'passwd' file as localhost:9292/passwd
Handlers can detect if bodies are a Rack::File, and use mechanisms like sendfile on the path
.
Constants
- ALLOWED_VERBS
- F
- SEPS
- VERSION
Attributes
cache_control[RW]
path[RW]
root[RW]
to_path[RW]
Public Class Methods
new(root, headers={}, default_mime = 'text/plain')
click to toggle source
# File lib/rack/tail_file.rb, line 28 def initialize(root, headers={}, default_mime = 'text/plain') @root = root @headers = headers @default_mime = default_mime end
Public Instance Methods
_call(env)
click to toggle source
# File lib/rack/tail_file.rb, line 40 def _call(env) return fail(405, "Method Not Allowed") unless method_allowed?(env) return fail(403, "Forbidden") unless path_is_within_root?(env) @path = file_path(env) if available? serving(env) else fail(404, "File not found: #{path_info_for(env)}") end end
available?()
click to toggle source
# File lib/rack/tail_file.rb, line 57 def available? begin F.file?(@path) && F.readable?(@path) rescue SystemCallError false end end
call(env)
click to toggle source
# File lib/rack/tail_file.rb, line 34 def call(env) dup._call(env) end
each() { |part| ... }
click to toggle source
# File lib/rack/tail_file.rb, line 147 def each F.open(@path, "rb") do |file| file.seek(@range.begin) remaining_len = @range.end-@range.begin+1 while remaining_len > 0 part = file.read([8192, remaining_len].min) break unless part remaining_len -= part.length yield part end end end
file_path(env)
click to toggle source
# File lib/rack/tail_file.rb, line 85 def file_path(env) target_file env end
method_allowed?(env)
click to toggle source
# File lib/rack/tail_file.rb, line 53 def method_allowed? env ALLOWED_VERBS.include? env["REQUEST_METHOD"] end
path_info_for(env)
click to toggle source
# File lib/rack/tail_file.rb, line 65 def path_info_for env Utils.unescape(env["PATH_INFO"]) end
path_is_within_root?(env)
click to toggle source
# File lib/rack/tail_file.rb, line 69 def path_is_within_root? env root = root env target = target_file env !target.relative_path_from(root).to_s.split(SEPS).any?{|p| p == ".."} end
requested_lines_size(env)
click to toggle source
# File lib/rack/tail_file.rb, line 105 def requested_lines_size(env) (env.fetch("QUERY_STRING")[/\d+/] || 50).to_i end
requested_size(env, response)
click to toggle source
# File lib/rack/tail_file.rb, line 123 def requested_size(env, response) # NOTE: # We check via File::size? whether this file provides size info # via stat (e.g. /proc files often don't), otherwise we have to # figure it out by reading the whole file into memory. size = F.size?(@path) || Utils.bytesize(F.read(@path)) #TODO handle invalid lines tail_size = tail_size_for requested_lines_size(env) if tail_size == size response[0] = 200 @range = 0..size-1 else start_byte = size - tail_size - 1 @range = start_byte..size-1 response[0] = 206 response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}" size = @range.end - @range.begin + 1 end size end
serving(env)
click to toggle source
# File lib/rack/tail_file.rb, line 89 def serving(env) last_modified = F.mtime(@path).httpdate return [304, {}, []] if env['HTTP_IF_MODIFIED_SINCE'] == last_modified headers = { "Last-Modified" => last_modified } mime = Mime.mime_type(F.extname(@path), @default_mime) headers["Content-Type"] = mime if mime # Set custom headers @headers.each { |field, content| headers[field] = content } if @headers response = [ 200, headers, env["REQUEST_METHOD"] == "HEAD" ? [] : self ] response[1]["Content-Length"] = requested_size(env, response).to_s response end
tail_size_for(line_count)
click to toggle source
# File lib/rack/tail_file.rb, line 109 def tail_size_for line_count elif = Elif.new(@path) tail_size = 0 line_count.times do begin tail_size += Rack::Utils.bytesize(elif.readline) rescue EOFError return tail_size end end tail_size - 1 # Don't include the first \n end
target_file(env)
click to toggle source
# File lib/rack/tail_file.rb, line 75 def target_file env path_info = Pathname.new("").join(*path_info_for(env).split(SEPS)) root = root env root.join(path_info) end
Private Instance Methods
fail(status, body)
click to toggle source
# File lib/rack/tail_file.rb, line 163 def fail(status, body) body += "\n" [ status, { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s, "X-Cascade" => "pass" }, [body] ] end