lib/rack/file.rb in rack-2.0.9.4 vs lib/rack/file.rb in rack-2.1.0

- old
+ new

@@ -1,176 +1,8 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' -require 'rack/request' -require 'rack/head' +# frozen_string_literal: true -module Rack - # 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 http://localhost:9292/passwd - # - # Handlers can detect if bodies are a Rack::File, and use mechanisms - # like sendfile on the +path+. +require 'rack/files' - class File - ALLOWED_VERBS = %w[GET HEAD OPTIONS] - ALLOW_HEADER = ALLOWED_VERBS.join(', ') - - attr_reader :root - - def initialize(root, headers={}, default_mime = 'text/plain') - @root = root - @headers = headers - @default_mime = default_mime - @head = Rack::Head.new(lambda { |env| get env }) - end - - def call(env) - # HEAD requests drop the response body, including 4xx error messages. - @head.call env - end - - def get(env) - request = Rack::Request.new env - unless ALLOWED_VERBS.include? request.request_method - return fail(405, "Method Not Allowed", {'Allow' => ALLOW_HEADER}) - end - - path_info = Utils.unescape_path request.path_info - return fail(400, "Bad Request") unless Utils.valid_path?(path_info) - - clean_path_info = Utils.clean_path_info(path_info) - path = ::File.join(@root, clean_path_info) - - available = begin - ::File.file?(path) && ::File.readable?(path) - rescue SystemCallError - false - end - - if available - serving(request, path) - else - fail(404, "File not found: #{path_info}") - end - end - - def serving(request, path) - if request.options? - return [200, {'Allow' => ALLOW_HEADER, CONTENT_LENGTH => '0'}, []] - end - last_modified = ::File.mtime(path).httpdate - return [304, {}, []] if request.get_header('HTTP_IF_MODIFIED_SINCE') == last_modified - - headers = { "Last-Modified" => last_modified } - mime_type = mime_type path, @default_mime - headers[CONTENT_TYPE] = mime_type if mime_type - - # Set custom headers - @headers.each { |field, content| headers[field] = content } if @headers - - response = [ 200, headers ] - - size = filesize path - - range = nil - ranges = Rack::Utils.get_byte_ranges(request.get_header('HTTP_RANGE'), size) - if ranges.nil? || ranges.length > 1 - # No ranges, or multiple ranges (which we don't support): - # TODO: Support multiple byte-ranges - response[0] = 200 - range = 0..size-1 - elsif ranges.empty? - # Unsatisfiable. Return error, and file size: - response = fail(416, "Byte range unsatisfiable") - response[1]["Content-Range"] = "bytes */#{size}" - return response - else - # Partial content: - range = ranges[0] - response[0] = 206 - response[1]["Content-Range"] = "bytes #{range.begin}-#{range.end}/#{size}" - size = range.end - range.begin + 1 - end - - response[2] = [response_body] unless response_body.nil? - - response[1][CONTENT_LENGTH] = size.to_s - response[2] = make_body request, path, range - response - end - - class Iterator - attr_reader :path, :range - alias :to_path :path - - def initialize path, range - @path = path - @range = range - end - - def each - ::File.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 - - def close; end - end - - private - - def make_body request, path, range - if request.head? - [] - else - Iterator.new path, range - end - end - - def fail(status, body, headers = {}) - body += "\n" - - [ - status, - { - CONTENT_TYPE => "text/plain", - CONTENT_LENGTH => body.size.to_s, - "X-Cascade" => "pass" - }.merge!(headers), - [body] - ] - end - - # The MIME type for the contents of the file located at @path - def mime_type path, default_mime - Mime.mime_type(::File.extname(path), default_mime) - end - - def filesize path - # If response_body is present, use its size. - return response_body.bytesize if response_body - - # 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. - ::File.size?(path) || ::File.read(path).bytesize - end - - # By default, the response body for file requests is nil. - # In this case, the response body will be generated later - # from the file at @path - def response_body - nil - end - end +module Rack + warn "Rack::File is deprecated, please use Rack::Files instead." + File = Files end