1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
| | # -*- encoding: binary -*-
require "time"
# used to serve local files, bypassing HTTP to the backend device
class Regurgitator::LocalFile # :nodoc:
# may be called by some Rack web servers to use sendfile(2)
attr_reader :to_path
# used to provide redirects to clients
attr_reader :uri
# Stores the Rack response (a 3-element Array) on success to simulate
# HTTP_Spew::Request objects
attr_reader :response
def initialize(env, path, uri, stat)
if modified_since = env["HTTP_IF_MODIFIED_SINCE"]
modified_since?(modified_since, stat) or return
end
size = stat.size
headers = {
"Content-Type" => "application/octet-stream".freeze, # always ".fid"
"Content-Length" => size.to_s,
"Last-Modified" => stat.mtime.httpdate,
"Accept-Ranges" => "bytes".freeze,
}
@response = [ 200, headers ]
ranges = Rack::Utils.get_byte_ranges(env['HTTP_RANGE'], size)
if nil == ranges || ranges.size > 1
@range = nil
elsif @range = ranges[0]
@response[0] = 206
headers["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}"
headers["Content-Length"] = (@range.end - @range.begin + 1).to_s
else
@response[0] = 416
headers["Content-Range"] = "bytes */#{size}"
headers["Content-Length"] = '0'.freeze
@response << []
return
end
case env["REQUEST_METHOD"]
when "GET"
@to_path, @uri = path, uri
@response << self
when "HEAD"
@response << []
else
raise "BUG: Unexpected REQUEST_METHOD=#{env['REQUEST_METHOD']}"
end
end
# normal Rack HTTP server endpoint, used if the server can't handle
# +to_path+
def each
File.open(@to_path) do |fp|
buf = ""
max = 0x4000
if @range
fp.seek(@range.begin)
len = @range.end - @range.begin + 1
while len > 0 && fp.read(len > max ? max : len, buf)
len -= buf.size
yield buf
end
else
while fp.read(0x4000, buf)
yield buf
end
end
buf.clear
end
end
def modified_since?(modified_since, stat)
begin
modified_since = Time.httpdate(modified_since)
rescue ArgumentError
h = { "Content-Type" => "text/plain", "Content-Length" => "0" }
@response = [ 400, h, [] ]
return false
end
stat.mtime > modified_since and return true
@response = [ 304, {}, [] ]
false
end
end
|