rack-cgi 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/rack/cgi/version.rb +1 -1
- data/lib/rack/cgi.rb +174 -1
- data/lib/rack-cgi.rb +1 -165
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4618ec968b358ef35f6d1ea3399aa48229fadaec
|
4
|
+
data.tar.gz: 17b358d43485c6cacb06d371f1ce7d6b8873436f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e401f35c930f09fbe29b44329b1c39b03be50df738891f2c1ab9b535d223c473cf3f92f82030a09c85fdd604c0717bafd88fbc209b49dad0fa4db8753c9814a
|
7
|
+
data.tar.gz: 8ef794dcd8a596bfda31a2967aba5583f6e0010af48e2320ecb00091383a6f5a334d201a23cef8b15206dcbcedf96ea6cb70264ca071091bbd9e538ff90c99ec
|
data/lib/rack/cgi/version.rb
CHANGED
data/lib/rack/cgi.rb
CHANGED
@@ -1 +1,174 @@
|
|
1
|
-
require '
|
1
|
+
require 'childprocess'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class CGI
|
5
|
+
class Executable
|
6
|
+
def self.=~ rhs
|
7
|
+
::File.executable? rhs
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize app, args = {}
|
12
|
+
@app = app
|
13
|
+
@opts = args.select {|k, _| k.is_a? Symbol}
|
14
|
+
@rules = args.select {|k, _| !k.is_a? Symbol}
|
15
|
+
|
16
|
+
@root = @opts[:cgi_path]
|
17
|
+
if @root !~ /^\//
|
18
|
+
@root = ::File.join(Dir.pwd, @root)
|
19
|
+
end
|
20
|
+
|
21
|
+
@index = @opts[:index] || []
|
22
|
+
@index = [@index] if not @index.is_a? Hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def solve_path path
|
26
|
+
path = ::File.join(@root, path)
|
27
|
+
if ::File.directory? path
|
28
|
+
@index.each do |f|
|
29
|
+
path2 = ::File.join(path, f)
|
30
|
+
return path2 if ::File.file? path2
|
31
|
+
end
|
32
|
+
else
|
33
|
+
return path if ::File.file? path
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def cgi_env env, path
|
39
|
+
env = env.select {|k, _| k =~ /^[A-Z]/}
|
40
|
+
env['SCRIPT_FILENAME'] = path
|
41
|
+
env['DOCUMENT_ROOT'] = @root
|
42
|
+
env['REDIRECT_STATUS'] = "200"
|
43
|
+
env
|
44
|
+
end
|
45
|
+
|
46
|
+
def match_cgi_rule path
|
47
|
+
@rules.each do |m, r|
|
48
|
+
if m =~ path
|
49
|
+
return r
|
50
|
+
end
|
51
|
+
end
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def run_cgi rule, path, env
|
56
|
+
if rule.empty?
|
57
|
+
args = [path]
|
58
|
+
else
|
59
|
+
args = [rule, path]
|
60
|
+
end
|
61
|
+
|
62
|
+
process = ChildProcess.build(*args)
|
63
|
+
process.io.stdout = Tempfile.new('rack-cgi-stdout')
|
64
|
+
process.io.stderr = Tempfile.new('rack-cgi-stderr')
|
65
|
+
env.each do |k, v|
|
66
|
+
process.environment[k] = v
|
67
|
+
end
|
68
|
+
process.cwd = Dir.pwd
|
69
|
+
process.start
|
70
|
+
process.wait
|
71
|
+
|
72
|
+
cont_out = ::File.read(process.io.stdout.path)
|
73
|
+
cont_err = ::File.read(process.io.stderr.path)
|
74
|
+
process.io.stdout.unlink
|
75
|
+
process.io.stderr.unlink
|
76
|
+
|
77
|
+
[process.exit_code, cont_out, cont_err]
|
78
|
+
end
|
79
|
+
|
80
|
+
def split_header_content output
|
81
|
+
lines = output.lines.to_a
|
82
|
+
header = []
|
83
|
+
|
84
|
+
until lines.empty?
|
85
|
+
l = lines.shift
|
86
|
+
if l == "\n" or l == "\r\n"
|
87
|
+
# find break line between header and content
|
88
|
+
return header.join, lines.join
|
89
|
+
elsif l =~ /:/
|
90
|
+
header << l
|
91
|
+
else
|
92
|
+
# content break header ruler, so deal as no header
|
93
|
+
return "", output
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# deal all content as header
|
98
|
+
return output, ""
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse_output output
|
102
|
+
header, content = split_header_content output
|
103
|
+
|
104
|
+
h = {}
|
105
|
+
header.each_line do |l|
|
106
|
+
k, v = l.split ':', 2
|
107
|
+
k.strip!
|
108
|
+
v.strip!
|
109
|
+
h[k.downcase] = [k, v]
|
110
|
+
end
|
111
|
+
|
112
|
+
if h['status']
|
113
|
+
status = h['status'][1].to_i
|
114
|
+
h.delete 'status'
|
115
|
+
else
|
116
|
+
status = 200
|
117
|
+
end
|
118
|
+
|
119
|
+
header_hash = Hash[h.values]
|
120
|
+
|
121
|
+
[status, header_hash, [content]]
|
122
|
+
end
|
123
|
+
|
124
|
+
def report_error code, out, err, cgi_env
|
125
|
+
h = {'Content-Type' => 'text/plain'}
|
126
|
+
status = 500
|
127
|
+
reports = []
|
128
|
+
|
129
|
+
reports << "CGI Error!\n\n"
|
130
|
+
reports << "stdout output:\n"
|
131
|
+
reports << out
|
132
|
+
reports << "\n"
|
133
|
+
reports << "stderr output:\n"
|
134
|
+
reports << err
|
135
|
+
reports << "\n"
|
136
|
+
reports << "environments:\n"
|
137
|
+
cgi_env.each do |k, v|
|
138
|
+
reports << "#{k} => #{v}\n"
|
139
|
+
end
|
140
|
+
|
141
|
+
[status, h, reports]
|
142
|
+
end
|
143
|
+
|
144
|
+
def call(env)
|
145
|
+
path = solve_path env["PATH_INFO"]
|
146
|
+
if not path
|
147
|
+
return @app.call(env)
|
148
|
+
end
|
149
|
+
|
150
|
+
rule = match_cgi_rule path
|
151
|
+
if not rule
|
152
|
+
return @app.call(env)
|
153
|
+
end
|
154
|
+
|
155
|
+
cgi_env = cgi_env(env, path)
|
156
|
+
code, out, err = run_cgi rule, path, cgi_env
|
157
|
+
if code == 0
|
158
|
+
parse_output out
|
159
|
+
else
|
160
|
+
report_error code, out, err, cgi_env
|
161
|
+
end
|
162
|
+
end
|
163
|
+
def run(env, rule, path)
|
164
|
+
cgi_env = cgi_env(env, path)
|
165
|
+
code, out, err = run_cgi rule, path, cgi_env
|
166
|
+
if code == 0
|
167
|
+
parse_output out
|
168
|
+
else
|
169
|
+
report_error code, out, err, cgi_env
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
data/lib/rack-cgi.rb
CHANGED
@@ -1,165 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Rack
|
4
|
-
class CGI
|
5
|
-
class Executable
|
6
|
-
def self.=~ rhs
|
7
|
-
::File.executable? rhs
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize app, args = {}
|
12
|
-
@app = app
|
13
|
-
@opts = args.select {|k, _| k.is_a? Symbol}
|
14
|
-
@rules = args.select {|k, _| !k.is_a? Symbol}
|
15
|
-
|
16
|
-
@root = @opts[:cgi_path]
|
17
|
-
if @root !~ /^\//
|
18
|
-
@root = ::File.join(Dir.pwd, @root)
|
19
|
-
end
|
20
|
-
|
21
|
-
@index = @opts[:index] || []
|
22
|
-
@index = [@index] if not @index.is_a? Hash
|
23
|
-
end
|
24
|
-
|
25
|
-
def solve_path path
|
26
|
-
path = ::File.join(@root, path)
|
27
|
-
if ::File.directory? path
|
28
|
-
@index.each do |f|
|
29
|
-
path2 = ::File.join(path, f)
|
30
|
-
return path2 if ::File.file? path2
|
31
|
-
end
|
32
|
-
else
|
33
|
-
return path if ::File.file? path
|
34
|
-
end
|
35
|
-
nil
|
36
|
-
end
|
37
|
-
|
38
|
-
def cgi_env env, path
|
39
|
-
env = env.select {|k, _| k =~ /^[A-Z]/}
|
40
|
-
env['SCRIPT_FILENAME'] = path
|
41
|
-
env['DOCUMENT_ROOT'] = @root
|
42
|
-
env['REDIRECT_STATUS'] = "200"
|
43
|
-
env
|
44
|
-
end
|
45
|
-
|
46
|
-
def match_cgi_rule path
|
47
|
-
@rules.each do |m, r|
|
48
|
-
if m =~ path
|
49
|
-
return r
|
50
|
-
end
|
51
|
-
end
|
52
|
-
return nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def run_cgi rule, path, env
|
56
|
-
if rule.empty?
|
57
|
-
args = [path]
|
58
|
-
else
|
59
|
-
args = [rule, path]
|
60
|
-
end
|
61
|
-
|
62
|
-
process = ChildProcess.build(*args)
|
63
|
-
process.io.stdout = Tempfile.new('rack-cgi-stdout')
|
64
|
-
process.io.stderr = Tempfile.new('rack-cgi-stderr')
|
65
|
-
env.each do |k, v|
|
66
|
-
process.environment[k] = v
|
67
|
-
end
|
68
|
-
process.cwd = Dir.pwd
|
69
|
-
process.start
|
70
|
-
process.wait
|
71
|
-
|
72
|
-
cont_out = ::File.read(process.io.stdout.path)
|
73
|
-
cont_err = ::File.read(process.io.stderr.path)
|
74
|
-
process.io.stdout.unlink
|
75
|
-
process.io.stderr.unlink
|
76
|
-
|
77
|
-
[process.exit_code, cont_out, cont_err]
|
78
|
-
end
|
79
|
-
|
80
|
-
def split_header_content output
|
81
|
-
lines = output.lines.to_a
|
82
|
-
header = []
|
83
|
-
|
84
|
-
until lines.empty?
|
85
|
-
l = lines.shift
|
86
|
-
if l == "\n" or l == "\r\n"
|
87
|
-
# find break line between header and content
|
88
|
-
return header.join, lines.join
|
89
|
-
elsif l =~ /:/
|
90
|
-
header << l
|
91
|
-
else
|
92
|
-
# content break header ruler, so deal as no header
|
93
|
-
return "", output
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# deal all content as header
|
98
|
-
return output, ""
|
99
|
-
end
|
100
|
-
|
101
|
-
def parse_output output
|
102
|
-
header, content = split_header_content output
|
103
|
-
|
104
|
-
h = {}
|
105
|
-
header.each_line do |l|
|
106
|
-
k, v = l.split ':', 2
|
107
|
-
k.strip!
|
108
|
-
v.strip!
|
109
|
-
h[k.downcase] = [k, v]
|
110
|
-
end
|
111
|
-
|
112
|
-
if h['status']
|
113
|
-
status = h['status'][1].to_i
|
114
|
-
h.delete 'status'
|
115
|
-
else
|
116
|
-
status = 200
|
117
|
-
end
|
118
|
-
|
119
|
-
header_hash = Hash[h.values]
|
120
|
-
|
121
|
-
[status, header_hash, [content]]
|
122
|
-
end
|
123
|
-
|
124
|
-
def report_error code, out, err, cgi_env
|
125
|
-
h = {'Content-Type' => 'text/plain'}
|
126
|
-
status = 500
|
127
|
-
reports = []
|
128
|
-
|
129
|
-
reports << "CGI Error!\n\n"
|
130
|
-
reports << "stdout output:\n"
|
131
|
-
reports << out
|
132
|
-
reports << "\n"
|
133
|
-
reports << "stderr output:\n"
|
134
|
-
reports << err
|
135
|
-
reports << "\n"
|
136
|
-
reports << "environments:\n"
|
137
|
-
cgi_env.each do |k, v|
|
138
|
-
reports << "#{k} => #{v}\n"
|
139
|
-
end
|
140
|
-
|
141
|
-
[status, h, reports]
|
142
|
-
end
|
143
|
-
|
144
|
-
def call(env)
|
145
|
-
path = solve_path env["PATH_INFO"]
|
146
|
-
if not path
|
147
|
-
return @app.call(env)
|
148
|
-
end
|
149
|
-
|
150
|
-
rule = match_cgi_rule path
|
151
|
-
if not rule
|
152
|
-
return @app.call(env)
|
153
|
-
end
|
154
|
-
|
155
|
-
cgi_env = cgi_env(env, path)
|
156
|
-
code, out, err = run_cgi rule, path, cgi_env
|
157
|
-
if code == 0
|
158
|
-
parse_output out
|
159
|
-
else
|
160
|
-
report_error code, out, err, cgi_env
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
1
|
+
require_relative 'rack/cgi'
|