diff options
Diffstat (limited to 'lib/unicorn/app/exec_cgi.rb')
-rw-r--r-- | lib/unicorn/app/exec_cgi.rb | 63 |
1 files changed, 31 insertions, 32 deletions
diff --git a/lib/unicorn/app/exec_cgi.rb b/lib/unicorn/app/exec_cgi.rb index 861d5e6..ef2a18e 100644 --- a/lib/unicorn/app/exec_cgi.rb +++ b/lib/unicorn/app/exec_cgi.rb @@ -1,11 +1,12 @@ +# -*- encoding: binary -*- + require 'unicorn' -require 'rack' module Unicorn::App # This class is highly experimental (even more so than the rest of Unicorn) # and has never run anything other than cgit. - class ExecCgi + class ExecCgi < Struct.new(:args) CHUNK_SIZE = 16384 PASS_VARS = %w( @@ -25,19 +26,19 @@ module Unicorn::App SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE - ).map { |x| x.freeze }.freeze # frozen strings are faster for Hash lookups + ).map { |x| x.freeze } # frozen strings are faster for Hash assignments # Intializes the app, example of usage in a config.ru # map "/cgit" do # run Unicorn::App::ExecCgi.new("/path/to/cgit.cgi") # end def initialize(*args) - @args = args.dup - first = @args[0] or + self.args = args + first = args[0] or raise ArgumentError, "need path to executable" - first[0..0] == "/" or @args[0] = ::File.expand_path(first) - File.executable?(@args[0]) or - raise ArgumentError, "#{@args[0]} is not executable" + first[0] == ?/ or args[0] = ::File.expand_path(first) + File.executable?(args[0]) or + raise ArgumentError, "#{args[0]} is not executable" end # Calls the app @@ -62,14 +63,14 @@ module Unicorn::App val = env[key] or next ENV[key] = val end - ENV['SCRIPT_NAME'] = @args[0] + ENV['SCRIPT_NAME'] = args[0] ENV['GATEWAY_INTERFACE'] = 'CGI/1.1' env.keys.grep(/^HTTP_/) { |key| ENV[key] = env[key] } a = IO.new(0).reopen(inp) b = IO.new(1).reopen(out) c = IO.new(2).reopen(err) - exec(*@args) + exec(*args) end # Extracts headers from CGI out, will change the offset of out. @@ -86,23 +87,24 @@ module Unicorn::App offset = 4 end offset += head.length - out.instance_variable_set('@unicorn_app_exec_cgi_offset', offset) - size -= offset # Allows +out+ to be used as a Rack body. - def out.each - sysseek(@unicorn_app_exec_cgi_offset) - - # don't use a preallocated buffer for sysread since we can't - # guarantee an actual socket is consuming the yielded string - # (or if somebody is pushing to an array for eventual concatenation - begin - yield(sysread(CHUNK_SIZE)) - rescue EOFError - return - end while true - end + out.instance_eval { class << self; self; end }.instance_eval { + define_method(:each) { |&blk| + sysseek(offset) + + # don't use a preallocated buffer for sysread since we can't + # guarantee an actual socket is consuming the yielded string + # (or if somebody is pushing to an array for eventual concatenation + begin + blk.call(sysread(CHUNK_SIZE)) + rescue EOFError + break + end while true + } + } + size -= offset prev = nil headers = Rack::Utils::HeaderHash.new head.split(/\r?\n/).each do |line| @@ -118,18 +120,15 @@ module Unicorn::App # ensures rack.input is a file handle that we can redirect stdin to def force_file_input(env) inp = env['rack.input'] - if inp.respond_to?(:fileno) && Integer === inp.fileno - inp - elsif inp.size == 0 # inp could be a StringIO or StringIO-like object + if inp.size == 0 # inp could be a StringIO or StringIO-like object ::File.open('/dev/null', 'rb') else tmp = Unicorn::Util.tmpio - # Rack::Lint::InputWrapper doesn't allow sysread :( - buf = Unicorn::Z.dup - while inp.read(CHUNK_SIZE, buf) + buf = inp.read(CHUNK_SIZE) + begin tmp.syswrite(buf) - end + end while inp.read(CHUNK_SIZE, buf) tmp.sysseek(0) tmp end @@ -141,7 +140,7 @@ module Unicorn::App err.seek(0) dst = env['rack.errors'] pid = status.pid - dst.write("#{pid}: #{@args.inspect} status=#{status} stderr:\n") + dst.write("#{pid}: #{args.inspect} status=#{status} stderr:\n") err.each_line { |line| dst.write("#{pid}: #{line}") } dst.flush end |