1 # -*- encoding: binary -*-
5 # Rack response middleware wrapping any IO-like object with an
6 # OS-level file descriptor associated with it. May also be used to
7 # create responses from integer file descriptors or existing +IO+
8 # objects. This may be used in conjunction with the #to_path method
9 # on servers that support it to pass arbitrary file descriptors into
10 # the HTTP response without additional open(2) syscalls
12 class DevFdResponse < Struct.new(:app, :to_io, :to_path)
15 # Rack middleware entry point, we'll just pass through responses
16 # unless they respond to +to_io+ or +to_path+
18 status, headers, body = response = app.call(env)
20 # totally uninteresting to us if there's no body
21 return response if STATUS_WITH_NO_ENTITY_BODY.include?(status)
23 io = body.to_io if body.respond_to?(:to_io)
24 io ||= File.open(body.to_path, 'rb') if body.respond_to?(:to_path)
25 return response if io.nil?
27 headers = HeaderHash.new(headers)
30 headers['Content-Length'] ||= st.size.to_s
31 headers.delete('Transfer-Encoding')
32 elsif st.pipe? || st.socket? # epoll-able things
33 if env['rainbows.autochunk']
34 headers['Transfer-Encoding'] = 'chunked'
35 headers.delete('Content-Length')
37 headers['X-Rainbows-Autochunk'] = 'no'
39 else # unlikely, char/block device file, directory, ...
42 resp = dup # be reentrant here
43 resp.to_path = "/dev/fd/#{io.fileno}"
45 [ status, headers.to_hash, resp ]
48 # called by the webserver or other middlewares if they can't
54 # remain Rack::Lint-compatible for people with wonky systems :P
55 unless File.exist?("/dev/fd/0")
56 alias to_path_orig to_path
60 # called by the web server after #each
63 to_io.close if to_io.respond_to?(:close)
64 rescue IOError # could've been IO::new()'ed and closed