regurgitator.git  about / heads / tags
Read-only Rack endpoints for MogileFS and BARFS
blob 52deaf8a7e575bdced4d2db5e50715d5f962e399 1620 bytes (raw)
$ git show HEAD:lib/regurgitator/file_request.rb	# shows this blob on the CLI

 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

git clone https://yhbt.net/regurgitator.git