fe62bd6b86ff5e625f654fab6d2060b5e0e257e9
[feedcatcher.git] / vendor / rails / actionpack / lib / action_controller / vendor / rack-1.0 / rack / file.rb
1 require 'time'
2 require 'rack/utils'
3 require 'rack/mime'
4
5 module Rack
6 # Rack::File serves files below the +root+ given, according to the
7 # path info of the Rack request.
8 #
9 # Handlers can detect if bodies are a Rack::File, and use mechanisms
10 # like sendfile on the +path+.
11
12 class File
13 attr_accessor :root
14 attr_accessor :path
15
16 alias :to_path :path
17
18 def initialize(root)
19 @root = root
20 end
21
22 def call(env)
23 dup._call(env)
24 end
25
26 F = ::File
27
28 def _call(env)
29 @path_info = Utils.unescape(env["PATH_INFO"])
30 return forbidden if @path_info.include? ".."
31
32 @path = F.join(@root, @path_info)
33
34 begin
35 if F.file?(@path) && F.readable?(@path)
36 serving
37 else
38 raise Errno::EPERM
39 end
40 rescue SystemCallError
41 not_found
42 end
43 end
44
45 def forbidden
46 body = "Forbidden\n"
47 [403, {"Content-Type" => "text/plain",
48 "Content-Length" => body.size.to_s},
49 [body]]
50 end
51
52 # NOTE:
53 # We check via File::size? whether this file provides size info
54 # via stat (e.g. /proc files often don't), otherwise we have to
55 # figure it out by reading the whole file into memory. And while
56 # we're at it we also use this as body then.
57
58 def serving
59 if size = F.size?(@path)
60 body = self
61 else
62 body = [F.read(@path)]
63 size = Utils.bytesize(body.first)
64 end
65
66 [200, {
67 "Last-Modified" => F.mtime(@path).httpdate,
68 "Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain'),
69 "Content-Length" => size.to_s
70 }, body]
71 end
72
73 def not_found
74 body = "File not found: #{@path_info}\n"
75 [404, {"Content-Type" => "text/plain",
76 "Content-Length" => body.size.to_s},
77 [body]]
78 end
79
80 def each
81 F.open(@path, "rb") { |file|
82 while part = file.read(8192)
83 yield part
84 end
85 }
86 end
87 end
88 end