Class: Rack::CGI

Inherits:
Object
  • Object
show all
Defined in:
lib/rack-cgi.rb

Defined Under Namespace

Classes: Executable

Instance Method Summary collapse

Constructor Details

#initialize(app, args = {}) ⇒ CGI

Returns a new instance of CGI.



12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/rack-cgi.rb', line 12

def initialize app, args = {}
  @app = app
  @opts = args.select {|k, _| k.is_a? Symbol}
  @rules = args.select {|k, _| !k.is_a? Symbol}

  @root = @opts[:cgi_path]
  if @root !~ /^\//
    @root = ::File.join(Dir.pwd, @root)
  end

  @index = @opts[:index] || []
  @index = [@index] if not @index.is_a? Hash
end

Instance Method Details

#call(env) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/rack-cgi.rb', line 145

def call(env)
  path = solve_path env["PATH_INFO"]
  if not path
    return @app.call(env)
  end

  rule = match_cgi_rule path
  if not rule
    return @app.call(env)
  end

  cgi_env = cgi_env(env, path)
  code, out, err = run_cgi rule, path, cgi_env
  if code == 0
    parse_output out
  else
    report_error code, out, err, cgi_env
  end
end

#cgi_env(env, path) ⇒ Object



39
40
41
42
43
44
45
# File 'lib/rack-cgi.rb', line 39

def cgi_env env, path
  env = env.select {|k, _| k =~ /^[A-Z]/}
  env['SCRIPT_FILENAME'] = path
  env['DOCUMENT_ROOT'] = @root
  env['REDIRECT_STATUS'] = "200"
  env
end

#match_cgi_rule(path) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/rack-cgi.rb', line 47

def match_cgi_rule path
  @rules.each do |m, r|
    if m =~ path
      return r
    end
  end
  return nil
end

#parse_output(output) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/rack-cgi.rb', line 102

def parse_output output
  header, content = split_header_content output
 
  h = {}
  header.each_line do |l|
    k, v = l.split ':', 2
    k.strip!
    v.strip!
    h[k.downcase] = [k, v]
  end

  if h['status']
    status = h['status'][1].to_i
    h.delete 'status'
  else
    status = 200
  end

  header_hash = Hash[h.values]

  [status, header_hash, [content]]
end

#report_error(code, out, err, cgi_env) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/rack-cgi.rb', line 125

def report_error code, out, err, cgi_env
  h = {'Content-Type' => 'text/plain'}
  status = 500
  reports = []
  
  reports << "CGI Error!\n\n"
  reports << "stdout output:\n"
  reports << out
  reports << "\n"
  reports << "stderr output:\n"
  reports << err
  reports << "\n"
  reports << "environments:\n"
  cgi_env.each do |k, v|
    reports << "#{k} => #{v}\n"
  end

  [status, h, reports]
end

#run_cgi(rule, path, env) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/rack-cgi.rb', line 56

def run_cgi rule, path, env
  if rule.empty?
    args = [path]
  else
    args = [rule, path]
  end

  process = ChildProcess.build(*args)
  process.io.stdout = Tempfile.new('rack-cgi-stdout')
  process.io.stderr = Tempfile.new('rack-cgi-stderr')
  env.each do |k, v|
    process.environment[k] = v
  end
  process.cwd = Dir.pwd
  process.start
  process.wait

  cont_out = ::File.read(process.io.stdout.path)
  cont_err = ::File.read(process.io.stderr.path)
  process.io.stdout.unlink
  process.io.stderr.unlink

  [process.exit_code, cont_out, cont_err]
end

#solve_path(path) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rack-cgi.rb', line 26

def solve_path path
  path = ::File.join(@root, path)
  if ::File.directory? path
    @index.each do |f|
      path2 = ::File.join(path, f)
      return path2 if ::File.file? path2
    end
  else
    return path if ::File.file? path
  end
  nil
end

#split_header_content(output) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/rack-cgi.rb', line 81

def split_header_content output
  lines = output.lines.to_a
  header = []
  
  until lines.empty? 
    l = lines.shift
    if l == "\n" or l == "\r\n"
      # find break line between header and content
      return header.join, lines.join
    elsif l =~ /:/
      header << l
    else
      # content break header ruler, so deal as no header
      return "", output
    end
  end

  # deal all content as header
  return output, ""
end