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
| | # -*- encoding: binary -*-
# Opens a IO.select-able object to read a (potentially large) file from a
# backend. We don't bother with keepalive since we want send buffers
# to be empty, so make sure you have enough local ports. You may want
# to enable TIME-WAIT reuse/recycling in your TCP stack if your
# network supports it. This object is used as a Rack response
# body.
class Regurgitator::FileRequest < HTTP_Spew::Request # :nodoc:
# used for doing redirects or reproxying with nginx
attr_reader :uri
extend Regurgitator::Local
USER_AGENT = name.dup
PASS_HEADERS = %w(Range If-Modified-Since Accept-Encoding)
PASS_HEADERS.map! { |x| "HTTP_#{x.tr!('a-z-', 'A-Z_')}".freeze }
# +uris+ may be an array, read-only URIs are at the end
def initialize(env, uris)
uri = uris[-1] # a subclass of this may call multiple URIs
addr = Socket.pack_sockaddr_in(uri.port, uri.host)
req = {
"REQUEST_URI" => uri.request_uri,
"HTTP_HOST" => "#{uri.host}:#{uri.port}",
}.merge!(env)
@uri = uri
super req, nil, addr
end
def self.run(env, uri_group, timeout = env["regurgitator.timeout"] || 5)
local_file = trylocal(env, uri_group) and return local_file.response
req = {
"REQUEST_METHOD" => env["REQUEST_METHOD"],
"HTTP_USER_AGENT" => USER_AGENT,
}
PASS_HEADERS.each { |k| pass = env[k] and req[k] = pass }
tmp = HTTP_Spew.wait(1, uri_group.map { |uris| new(req, uris) }, timeout)
tmp.delete_if(&:error)
ready = tmp.shift or raise Regurgitator::NoDevices, "no readable devices"
tmp.each(&:close)
ready.response
end
end
|