Parent

Unicorn::App::ExecCgi

This class is highly experimental (even more so than the rest of Unicorn) and has never run anything other than cgit.

Constants

CHUNK_SIZE
(Not documented)
PASS_VARS
(Not documented)

Public Class Methods

new(*args) click to toggle source

Intializes the app, example of usage in a config.ru

  map "/cgit" do
    run Unicorn::App::ExecCgi.new("/path/to/cgit.cgi")
  end
    # File lib/unicorn/app/exec_cgi.rb, line 35
35:     def initialize(*args)
36:       self.args = args
37:       first = args[0] or
38:         raise ArgumentError, "need path to executable"
39:       first[0] == ?/ or args[0] = ::File.expand_path(first)
40:       File.executable?(args[0]) or
41:         raise ArgumentError, "#{args[0]} is not executable"
42:     end

Public Instance Methods

call(env) click to toggle source

Calls the app

    # File lib/unicorn/app/exec_cgi.rb, line 45
45:     def call(env)
46:       out, err = Unicorn::Util.tmpio, Unicorn::Util.tmpio
47:       inp = force_file_input(env)
48:       pid = fork { run_child(inp, out, err, env) }
49:       inp.close
50:       pid, status = Process.waitpid2(pid)
51:       write_errors(env, err, status) if err.stat.size > 0
52:       err.close
53: 
54:       return parse_output!(out) if status.success?
55:       out.close
56:       [ 500, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ]
57:     end

Private Instance Methods

force_file_input(env) click to toggle source

ensures rack.input is a file handle that we can redirect stdin to

     # File lib/unicorn/app/exec_cgi.rb, line 121
121:     def force_file_input(env)
122:       inp = env['rack.input']
123:       if inp.size == 0 # inp could be a StringIO or StringIO-like object
124:         ::File.open('/dev/null', 'rb')
125:       else
126:         tmp = Unicorn::Util.tmpio
127: 
128:         buf = inp.read(CHUNK_SIZE)
129:         begin
130:           tmp.syswrite(buf)
131:         end while inp.read(CHUNK_SIZE, buf)
132:         tmp.sysseek(0)
133:         tmp
134:       end
135:     end
parse_output!(out) click to toggle source

Extracts headers from CGI out, will change the offset of out. This returns a standard Rack-compatible return value:

  [ 200, HeadersHash, body ]
     # File lib/unicorn/app/exec_cgi.rb, line 79
 79:     def parse_output!(out)
 80:       size = out.stat.size
 81:       out.sysseek(0)
 82:       head = out.sysread(CHUNK_SIZE)
 83:       offset = 2
 84:       head, body = head.split(/\n\n/, 2)
 85:       if body.nil?
 86:         head, body = head.split(/\r\n\r\n/, 2)
 87:         offset = 4
 88:       end
 89:       offset += head.length
 90: 
 91:       # Allows +out+ to be used as a Rack body.
 92:       out.instance_eval { class << self; self; end }.instance_eval {
 93:         define_method(:each) { |&blk|
 94:           sysseek(offset)
 95: 
 96:           # don't use a preallocated buffer for sysread since we can't
 97:           # guarantee an actual socket is consuming the yielded string
 98:           # (or if somebody is pushing to an array for eventual concatenation
 99:           begin
100:             blk.call(sysread(CHUNK_SIZE))
101:           rescue EOFError
102:             break
103:           end while true
104:         }
105:       }
106: 
107:       size -= offset
108:       prev = nil
109:       headers = Rack::Utils::HeaderHash.new
110:       head.split(/\r?\n/).each do |line|
111:         case line
112:         when /^([A-Za-z0-9-]+):\s*(.*)$/ then headers[prev = $1] = $2
113:         when /^[ \t]/ then headers[prev] << "\n#{line}" if prev
114:         end
115:       end
116:       headers['Content-Length'] = size.to_s
117:       [ 200, headers, out ]
118:     end
run_child(inp, out, err, env) click to toggle source

(Not documented)

    # File lib/unicorn/app/exec_cgi.rb, line 61
61:     def run_child(inp, out, err, env)
62:       PASS_VARS.each do |key|
63:         val = env[key] or next
64:         ENV[key] = val
65:       end
66:       ENV['SCRIPT_NAME'] = args[0]
67:       ENV['GATEWAY_INTERFACE'] = 'CGI/1.1'
68:       env.keys.grep(/^HTTP_/) { |key| ENV[key] = env[key] }
69: 
70:       a = IO.new(0).reopen(inp)
71:       b = IO.new(1).reopen(out)
72:       c = IO.new(2).reopen(err)
73:       exec(*args)
74:     end
write_errors(env, err, status) click to toggle source

rack.errors this may not be an IO object, so we couldn’t just redirect the CGI executable to that earlier.

     # File lib/unicorn/app/exec_cgi.rb, line 139
139:     def write_errors(env, err, status)
140:       err.seek(0)
141:       dst = env['rack.errors']
142:       pid = status.pid
143:       dst.write("#{pid}: #{args.inspect} status=#{status} stderr:\n")
144:       err.each_line { |line| dst.write("#{pid}: #{line}") }
145:       dst.flush
146:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.