unicorn-fotopedia 0.99.1
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.
- data/.CHANGELOG.old +25 -0
- data/.document +19 -0
- data/.gitignore +21 -0
- data/.mailmap +26 -0
- data/CONTRIBUTORS +32 -0
- data/COPYING +339 -0
- data/DESIGN +105 -0
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +30 -0
- data/Documentation/unicorn.1.txt +171 -0
- data/Documentation/unicorn_rails.1.txt +172 -0
- data/FAQ +52 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +292 -0
- data/HACKING +116 -0
- data/ISSUES +36 -0
- data/KNOWN_ISSUES +50 -0
- data/LICENSE +55 -0
- data/PHILOSOPHY +145 -0
- data/README +149 -0
- data/Rakefile +191 -0
- data/SIGNALS +109 -0
- data/Sandbox +78 -0
- data/TODO +5 -0
- data/TUNING +70 -0
- data/bin/unicorn +126 -0
- data/bin/unicorn_rails +203 -0
- data/examples/big_app_gc.rb +33 -0
- data/examples/echo.ru +27 -0
- data/examples/git.ru +13 -0
- data/examples/init.sh +58 -0
- data/examples/logger_mp_safe.rb +25 -0
- data/examples/nginx.conf +139 -0
- data/examples/unicorn.conf.rb +78 -0
- data/ext/unicorn_http/CFLAGS +13 -0
- data/ext/unicorn_http/c_util.h +124 -0
- data/ext/unicorn_http/common_field_optimization.h +111 -0
- data/ext/unicorn_http/ext_help.h +77 -0
- data/ext/unicorn_http/extconf.rb +14 -0
- data/ext/unicorn_http/global_variables.h +89 -0
- data/ext/unicorn_http/unicorn_http.rl +714 -0
- data/ext/unicorn_http/unicorn_http_common.rl +75 -0
- data/lib/unicorn.rb +847 -0
- data/lib/unicorn/app/exec_cgi.rb +150 -0
- data/lib/unicorn/app/inetd.rb +109 -0
- data/lib/unicorn/app/old_rails.rb +33 -0
- data/lib/unicorn/app/old_rails/static.rb +58 -0
- data/lib/unicorn/cgi_wrapper.rb +145 -0
- data/lib/unicorn/configurator.rb +421 -0
- data/lib/unicorn/const.rb +34 -0
- data/lib/unicorn/http_request.rb +72 -0
- data/lib/unicorn/http_response.rb +75 -0
- data/lib/unicorn/launcher.rb +65 -0
- data/lib/unicorn/oob_gc.rb +58 -0
- data/lib/unicorn/socket_helper.rb +152 -0
- data/lib/unicorn/tee_input.rb +217 -0
- data/lib/unicorn/util.rb +90 -0
- data/local.mk.sample +62 -0
- data/setup.rb +1586 -0
- data/t/.gitignore +2 -0
- data/t/GNUmakefile +67 -0
- data/t/README +42 -0
- data/t/bin/content-md5-put +36 -0
- data/t/bin/sha1sum.rb +23 -0
- data/t/bin/unused_listen +40 -0
- data/t/bin/utee +12 -0
- data/t/env.ru +3 -0
- data/t/my-tap-lib.sh +200 -0
- data/t/t0000-http-basic.sh +50 -0
- data/t/t0001-reload-bad-config.sh +52 -0
- data/t/t0002-config-conflict.sh +49 -0
- data/t/test-lib.sh +100 -0
- data/test/aggregate.rb +15 -0
- data/test/benchmark/README +50 -0
- data/test/benchmark/dd.ru +18 -0
- data/test/exec/README +5 -0
- data/test/exec/test_exec.rb +1038 -0
- data/test/rails/app-1.2.3/.gitignore +2 -0
- data/test/rails/app-1.2.3/Rakefile +7 -0
- data/test/rails/app-1.2.3/app/controllers/application.rb +6 -0
- data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-1.2.3/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-1.2.3/config/boot.rb +11 -0
- data/test/rails/app-1.2.3/config/database.yml +12 -0
- data/test/rails/app-1.2.3/config/environment.rb +13 -0
- data/test/rails/app-1.2.3/config/environments/development.rb +9 -0
- data/test/rails/app-1.2.3/config/environments/production.rb +5 -0
- data/test/rails/app-1.2.3/config/routes.rb +6 -0
- data/test/rails/app-1.2.3/db/.gitignore +0 -0
- data/test/rails/app-1.2.3/public/404.html +1 -0
- data/test/rails/app-1.2.3/public/500.html +1 -0
- data/test/rails/app-2.0.2/.gitignore +2 -0
- data/test/rails/app-2.0.2/Rakefile +7 -0
- data/test/rails/app-2.0.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.0.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.0.2/config/boot.rb +11 -0
- data/test/rails/app-2.0.2/config/database.yml +12 -0
- data/test/rails/app-2.0.2/config/environment.rb +17 -0
- data/test/rails/app-2.0.2/config/environments/development.rb +8 -0
- data/test/rails/app-2.0.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.0.2/config/routes.rb +6 -0
- data/test/rails/app-2.0.2/db/.gitignore +0 -0
- data/test/rails/app-2.0.2/public/404.html +1 -0
- data/test/rails/app-2.0.2/public/500.html +1 -0
- data/test/rails/app-2.1.2/.gitignore +2 -0
- data/test/rails/app-2.1.2/Rakefile +7 -0
- data/test/rails/app-2.1.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.1.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.1.2/config/boot.rb +111 -0
- data/test/rails/app-2.1.2/config/database.yml +12 -0
- data/test/rails/app-2.1.2/config/environment.rb +17 -0
- data/test/rails/app-2.1.2/config/environments/development.rb +7 -0
- data/test/rails/app-2.1.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.1.2/config/routes.rb +6 -0
- data/test/rails/app-2.1.2/db/.gitignore +0 -0
- data/test/rails/app-2.1.2/public/404.html +1 -0
- data/test/rails/app-2.1.2/public/500.html +1 -0
- data/test/rails/app-2.2.2/.gitignore +2 -0
- data/test/rails/app-2.2.2/Rakefile +7 -0
- data/test/rails/app-2.2.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.2.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.2.2/config/boot.rb +111 -0
- data/test/rails/app-2.2.2/config/database.yml +12 -0
- data/test/rails/app-2.2.2/config/environment.rb +17 -0
- data/test/rails/app-2.2.2/config/environments/development.rb +7 -0
- data/test/rails/app-2.2.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.2.2/config/routes.rb +6 -0
- data/test/rails/app-2.2.2/db/.gitignore +0 -0
- data/test/rails/app-2.2.2/public/404.html +1 -0
- data/test/rails/app-2.2.2/public/500.html +1 -0
- data/test/rails/app-2.3.5/.gitignore +2 -0
- data/test/rails/app-2.3.5/Rakefile +7 -0
- data/test/rails/app-2.3.5/app/controllers/application_controller.rb +5 -0
- data/test/rails/app-2.3.5/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.3.5/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.3.5/config/boot.rb +109 -0
- data/test/rails/app-2.3.5/config/database.yml +12 -0
- data/test/rails/app-2.3.5/config/environment.rb +17 -0
- data/test/rails/app-2.3.5/config/environments/development.rb +7 -0
- data/test/rails/app-2.3.5/config/environments/production.rb +6 -0
- data/test/rails/app-2.3.5/config/routes.rb +6 -0
- data/test/rails/app-2.3.5/db/.gitignore +0 -0
- data/test/rails/app-2.3.5/public/404.html +1 -0
- data/test/rails/app-2.3.5/public/500.html +1 -0
- data/test/rails/app-2.3.5/public/x.txt +1 -0
- data/test/rails/test_rails.rb +280 -0
- data/test/test_helper.rb +301 -0
- data/test/unit/test_configurator.rb +150 -0
- data/test/unit/test_http_parser.rb +555 -0
- data/test/unit/test_http_parser_ng.rb +443 -0
- data/test/unit/test_request.rb +184 -0
- data/test/unit/test_response.rb +110 -0
- data/test/unit/test_server.rb +291 -0
- data/test/unit/test_signals.rb +206 -0
- data/test/unit/test_socket_helper.rb +147 -0
- data/test/unit/test_tee_input.rb +257 -0
- data/test/unit/test_upload.rb +298 -0
- data/test/unit/test_util.rb +96 -0
- data/unicorn.gemspec +52 -0
- metadata +283 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
require 'unicorn'
|
4
|
+
|
5
|
+
module Unicorn::App
|
6
|
+
|
7
|
+
# This class is highly experimental (even more so than the rest of Unicorn)
|
8
|
+
# and has never run anything other than cgit.
|
9
|
+
class ExecCgi < Struct.new(:args)
|
10
|
+
|
11
|
+
CHUNK_SIZE = 16384
|
12
|
+
PASS_VARS = %w(
|
13
|
+
CONTENT_LENGTH
|
14
|
+
CONTENT_TYPE
|
15
|
+
GATEWAY_INTERFACE
|
16
|
+
AUTH_TYPE
|
17
|
+
PATH_INFO
|
18
|
+
PATH_TRANSLATED
|
19
|
+
QUERY_STRING
|
20
|
+
REMOTE_ADDR
|
21
|
+
REMOTE_HOST
|
22
|
+
REMOTE_IDENT
|
23
|
+
REMOTE_USER
|
24
|
+
REQUEST_METHOD
|
25
|
+
SERVER_NAME
|
26
|
+
SERVER_PORT
|
27
|
+
SERVER_PROTOCOL
|
28
|
+
SERVER_SOFTWARE
|
29
|
+
).map { |x| x.freeze } # frozen strings are faster for Hash assignments
|
30
|
+
|
31
|
+
# Intializes the app, example of usage in a config.ru
|
32
|
+
# map "/cgit" do
|
33
|
+
# run Unicorn::App::ExecCgi.new("/path/to/cgit.cgi")
|
34
|
+
# end
|
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
|
43
|
+
|
44
|
+
# Calls the app
|
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
|
58
|
+
|
59
|
+
private
|
60
|
+
|
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
|
75
|
+
|
76
|
+
# Extracts headers from CGI out, will change the offset of out.
|
77
|
+
# This returns a standard Rack-compatible return value:
|
78
|
+
# [ 200, HeadersHash, body ]
|
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
|
119
|
+
|
120
|
+
# ensures rack.input is a file handle that we can redirect stdin to
|
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
|
136
|
+
|
137
|
+
# rack.errors this may not be an IO object, so we couldn't
|
138
|
+
# just redirect the CGI executable to that earlier.
|
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
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2009 Eric Wong
|
4
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
5
|
+
|
6
|
+
# this class *must* be used with Rack::Chunked
|
7
|
+
|
8
|
+
module Unicorn::App
|
9
|
+
class Inetd < Struct.new(:cmd)
|
10
|
+
|
11
|
+
class CatBody < Struct.new(:errors, :err_rd, :out_rd, :pid_map)
|
12
|
+
def initialize(env, cmd)
|
13
|
+
self.errors = env['rack.errors']
|
14
|
+
in_rd, in_wr = IO.pipe
|
15
|
+
self.err_rd, err_wr = IO.pipe
|
16
|
+
self.out_rd, out_wr = IO.pipe
|
17
|
+
|
18
|
+
cmd_pid = fork {
|
19
|
+
inp, out, err = (0..2).map { |i| IO.new(i) }
|
20
|
+
inp.reopen(in_rd)
|
21
|
+
out.reopen(out_wr)
|
22
|
+
err.reopen(err_wr)
|
23
|
+
[ in_rd, in_wr, err_rd, err_wr, out_rd, out_wr ].each { |i| i.close }
|
24
|
+
exec(*cmd)
|
25
|
+
}
|
26
|
+
[ in_rd, err_wr, out_wr ].each { |io| io.close }
|
27
|
+
[ in_wr, err_rd, out_rd ].each { |io| io.binmode }
|
28
|
+
in_wr.sync = true
|
29
|
+
|
30
|
+
# Unfortunately, input here must be processed inside a seperate
|
31
|
+
# thread/process using blocking I/O since env['rack.input'] is not
|
32
|
+
# IO.select-able and attempting to make it so would trip Rack::Lint
|
33
|
+
inp_pid = fork {
|
34
|
+
input = env['rack.input']
|
35
|
+
[ err_rd, out_rd ].each { |io| io.close }
|
36
|
+
|
37
|
+
# this is dependent on input.read having readpartial semantics:
|
38
|
+
buf = input.read(16384)
|
39
|
+
begin
|
40
|
+
in_wr.write(buf)
|
41
|
+
end while input.read(16384, buf)
|
42
|
+
}
|
43
|
+
in_wr.close
|
44
|
+
self.pid_map = {
|
45
|
+
inp_pid => 'input streamer',
|
46
|
+
cmd_pid => cmd.inspect,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def each(&block)
|
51
|
+
begin
|
52
|
+
rd, = IO.select([err_rd, out_rd])
|
53
|
+
rd && rd.first or next
|
54
|
+
|
55
|
+
if rd.include?(err_rd)
|
56
|
+
begin
|
57
|
+
errors.write(err_rd.read_nonblock(16384))
|
58
|
+
rescue Errno::EINTR
|
59
|
+
rescue Errno::EAGAIN
|
60
|
+
break
|
61
|
+
end while true
|
62
|
+
end
|
63
|
+
|
64
|
+
rd.include?(out_rd) or next
|
65
|
+
|
66
|
+
begin
|
67
|
+
yield out_rd.read_nonblock(16384)
|
68
|
+
rescue Errno::EINTR
|
69
|
+
rescue Errno::EAGAIN
|
70
|
+
break
|
71
|
+
end while true
|
72
|
+
rescue EOFError,Errno::EPIPE,Errno::EBADF,Errno::EINVAL
|
73
|
+
break
|
74
|
+
end while true
|
75
|
+
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def close
|
80
|
+
pid_map.each { |pid, str|
|
81
|
+
begin
|
82
|
+
pid, status = Process.waitpid2(pid)
|
83
|
+
status.success? or
|
84
|
+
errors.write("#{str}: #{status.inspect} (PID:#{pid})\n")
|
85
|
+
rescue Errno::ECHILD
|
86
|
+
errors.write("Failed to reap #{str} (PID:#{pid})\n")
|
87
|
+
end
|
88
|
+
}
|
89
|
+
out_rd.close
|
90
|
+
err_rd.close
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def initialize(*cmd)
|
96
|
+
self.cmd = cmd
|
97
|
+
end
|
98
|
+
|
99
|
+
def call(env)
|
100
|
+
/\A100-continue\z/i =~ env[Unicorn::Const::HTTP_EXPECT] and
|
101
|
+
return [ 100, {} , [] ]
|
102
|
+
|
103
|
+
[ 200, { 'Content-Type' => 'application/octet-stream' },
|
104
|
+
CatBody.new(env, cmd) ]
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# This code is based on the original Rails handler in Mongrel
|
4
|
+
# Copyright (c) 2005 Zed A. Shaw
|
5
|
+
# Copyright (c) 2009 Eric Wong
|
6
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
7
|
+
# Additional work donated by contributors. See CONTRIBUTORS for more info.
|
8
|
+
require 'unicorn/cgi_wrapper'
|
9
|
+
require 'dispatcher'
|
10
|
+
|
11
|
+
module Unicorn; module App; end; end
|
12
|
+
|
13
|
+
# Implements a handler that can run Rails.
|
14
|
+
class Unicorn::App::OldRails
|
15
|
+
|
16
|
+
autoload :Static, "unicorn/app/old_rails/static"
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
cgi = Unicorn::CGIWrapper.new(env)
|
20
|
+
begin
|
21
|
+
Dispatcher.dispatch(cgi,
|
22
|
+
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS,
|
23
|
+
cgi.body)
|
24
|
+
rescue => e
|
25
|
+
err = env['rack.errors']
|
26
|
+
err.write("#{e} #{e.message}\n")
|
27
|
+
e.backtrace.each { |line| err.write("#{line}\n") }
|
28
|
+
end
|
29
|
+
cgi.out # finalize the response
|
30
|
+
cgi.rack_response
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# This code is based on the original Rails handler in Mongrel
|
4
|
+
# Copyright (c) 2005 Zed A. Shaw
|
5
|
+
# Copyright (c) 2009 Eric Wong
|
6
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
7
|
+
|
8
|
+
# Static file handler for Rails < 2.3. This handler is only provided
|
9
|
+
# as a convenience for developers. Performance-minded deployments should
|
10
|
+
# use nginx (or similar) for serving static files.
|
11
|
+
#
|
12
|
+
# This supports page caching directly and will try to resolve a
|
13
|
+
# request in the following order:
|
14
|
+
#
|
15
|
+
# * If the requested exact PATH_INFO exists as a file then serve it.
|
16
|
+
# * If it exists at PATH_INFO+rest_operator+".html" exists
|
17
|
+
# then serve that.
|
18
|
+
#
|
19
|
+
# This means that if you are using page caching it will actually work
|
20
|
+
# with Unicorn and you should see a decent speed boost (but not as
|
21
|
+
# fast as if you use a static server like nginx).
|
22
|
+
class Unicorn::App::OldRails::Static < Struct.new(:app, :root, :file_server)
|
23
|
+
FILE_METHODS = { 'GET' => true, 'HEAD' => true }
|
24
|
+
|
25
|
+
# avoid allocating new strings for hash lookups
|
26
|
+
REQUEST_METHOD = 'REQUEST_METHOD'
|
27
|
+
REQUEST_URI = 'REQUEST_URI'
|
28
|
+
PATH_INFO = 'PATH_INFO'
|
29
|
+
|
30
|
+
def initialize(app)
|
31
|
+
self.app = app
|
32
|
+
self.root = "#{::RAILS_ROOT}/public"
|
33
|
+
self.file_server = ::Rack::File.new(root)
|
34
|
+
end
|
35
|
+
|
36
|
+
def call(env)
|
37
|
+
# short circuit this ASAP if serving non-file methods
|
38
|
+
FILE_METHODS.include?(env[REQUEST_METHOD]) or return app.call(env)
|
39
|
+
|
40
|
+
# first try the path as-is
|
41
|
+
path_info = env[PATH_INFO].chomp("/")
|
42
|
+
if File.file?("#{root}/#{::Rack::Utils.unescape(path_info)}")
|
43
|
+
# File exists as-is so serve it up
|
44
|
+
env[PATH_INFO] = path_info
|
45
|
+
return file_server.call(env)
|
46
|
+
end
|
47
|
+
|
48
|
+
# then try the cached version:
|
49
|
+
path_info << ActionController::Base.page_cache_extension
|
50
|
+
|
51
|
+
if File.file?("#{root}/#{::Rack::Utils.unescape(path_info)}")
|
52
|
+
env[PATH_INFO] = path_info
|
53
|
+
return file_server.call(env)
|
54
|
+
end
|
55
|
+
|
56
|
+
app.call(env) # call OldRails
|
57
|
+
end
|
58
|
+
end if defined?(Unicorn::App::OldRails)
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# This code is based on the original CGIWrapper from Mongrel
|
4
|
+
# Copyright (c) 2005 Zed A. Shaw
|
5
|
+
# Copyright (c) 2009 Eric Wong
|
6
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
7
|
+
#
|
8
|
+
# Additional work donated by contributors. See CONTRIBUTORS for more info.
|
9
|
+
|
10
|
+
require 'cgi'
|
11
|
+
|
12
|
+
module Unicorn; end
|
13
|
+
|
14
|
+
# The beginning of a complete wrapper around Unicorn's internal HTTP
|
15
|
+
# processing system but maintaining the original Ruby CGI module. Use
|
16
|
+
# this only as a crutch to get existing CGI based systems working. It
|
17
|
+
# should handle everything, but please notify us if you see special
|
18
|
+
# warnings. This work is still very alpha so we need testers to help
|
19
|
+
# work out the various corner cases.
|
20
|
+
class Unicorn::CGIWrapper < ::CGI
|
21
|
+
undef_method :env_table
|
22
|
+
attr_reader :env_table
|
23
|
+
attr_reader :body
|
24
|
+
|
25
|
+
# these are stripped out of any keys passed to CGIWrapper.header function
|
26
|
+
NPH = 'nph'.freeze # Completely ignored, Unicorn outputs the date regardless
|
27
|
+
CONNECTION = 'connection'.freeze # Completely ignored. Why is CGI doing this?
|
28
|
+
CHARSET = 'charset'.freeze # this gets appended to Content-Type
|
29
|
+
COOKIE = 'cookie'.freeze # maps (Hash,Array,String) to "Set-Cookie" headers
|
30
|
+
STATUS = 'status'.freeze # stored as @status
|
31
|
+
Status = 'Status'.freeze # code + human-readable text, Rails sets this
|
32
|
+
|
33
|
+
# some of these are common strings, but this is the only module
|
34
|
+
# using them and the reason they're not in Unicorn::Const
|
35
|
+
SET_COOKIE = 'Set-Cookie'.freeze
|
36
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
37
|
+
CONTENT_LENGTH = 'Content-Length'.freeze # this is NOT Const::CONTENT_LENGTH
|
38
|
+
RACK_INPUT = 'rack.input'.freeze
|
39
|
+
RACK_ERRORS = 'rack.errors'.freeze
|
40
|
+
|
41
|
+
# this maps CGI header names to HTTP header names
|
42
|
+
HEADER_MAP = {
|
43
|
+
'status' => Status,
|
44
|
+
'type' => CONTENT_TYPE,
|
45
|
+
'server' => 'Server'.freeze,
|
46
|
+
'language' => 'Content-Language'.freeze,
|
47
|
+
'expires' => 'Expires'.freeze,
|
48
|
+
'length' => CONTENT_LENGTH,
|
49
|
+
}
|
50
|
+
|
51
|
+
# Takes an a Rackable environment, plus any additional CGI.new
|
52
|
+
# arguments These are used internally to create a wrapper around the
|
53
|
+
# real CGI while maintaining Rack/Unicorn's view of the world. This
|
54
|
+
# this will NOT deal well with large responses that take up a lot of
|
55
|
+
# memory, but neither does the CGI nor the original CGIWrapper from
|
56
|
+
# Mongrel...
|
57
|
+
def initialize(rack_env, *args)
|
58
|
+
@env_table = rack_env
|
59
|
+
@status = nil
|
60
|
+
@head = {}
|
61
|
+
@headv = Hash.new { |hash,key| hash[key] = [] }
|
62
|
+
@body = StringIO.new("")
|
63
|
+
super(*args)
|
64
|
+
end
|
65
|
+
|
66
|
+
# finalizes the response in a way Rack applications would expect
|
67
|
+
def rack_response
|
68
|
+
# @head[CONTENT_LENGTH] ||= @body.size
|
69
|
+
@headv[SET_COOKIE].concat(@output_cookies) if @output_cookies
|
70
|
+
@headv.each_pair do |key,value|
|
71
|
+
@head[key] ||= value.join("\n") unless value.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
# Capitalized "Status:", with human-readable status code (e.g. "200 OK")
|
75
|
+
@status ||= @head.delete(Status)
|
76
|
+
|
77
|
+
[ @status || 500, @head, [ @body.string ] ]
|
78
|
+
end
|
79
|
+
|
80
|
+
# The header is typically called to send back the header. In our case we
|
81
|
+
# collect it into a hash for later usage. This can be called multiple
|
82
|
+
# times to set different cookies.
|
83
|
+
def header(options = "text/html")
|
84
|
+
# if they pass in a string then just write the Content-Type
|
85
|
+
if String === options
|
86
|
+
@head[CONTENT_TYPE] ||= options
|
87
|
+
else
|
88
|
+
HEADER_MAP.each_pair do |from, to|
|
89
|
+
from = options.delete(from) or next
|
90
|
+
@head[to] = from.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
@head[CONTENT_TYPE] ||= "text/html"
|
94
|
+
if charset = options.delete(CHARSET)
|
95
|
+
@head[CONTENT_TYPE] << "; charset=#{charset}"
|
96
|
+
end
|
97
|
+
|
98
|
+
# lots of ways to set cookies
|
99
|
+
if cookie = options.delete(COOKIE)
|
100
|
+
set_cookies = @headv[SET_COOKIE]
|
101
|
+
case cookie
|
102
|
+
when Array
|
103
|
+
cookie.each { |c| set_cookies << c.to_s }
|
104
|
+
when Hash
|
105
|
+
cookie.each_value { |c| set_cookies << c.to_s }
|
106
|
+
else
|
107
|
+
set_cookies << cookie.to_s
|
108
|
+
end
|
109
|
+
end
|
110
|
+
@status ||= options.delete(STATUS) # all lower-case
|
111
|
+
|
112
|
+
# drop the keys we don't want anymore
|
113
|
+
options.delete(NPH)
|
114
|
+
options.delete(CONNECTION)
|
115
|
+
|
116
|
+
# finally, set the rest of the headers as-is, allowing duplicates
|
117
|
+
options.each_pair { |k,v| @headv[k] << v }
|
118
|
+
end
|
119
|
+
|
120
|
+
# doing this fakes out the cgi library to think the headers are empty
|
121
|
+
# we then do the real headers in the out function call later
|
122
|
+
""
|
123
|
+
end
|
124
|
+
|
125
|
+
# The dumb thing is people can call header or this or both and in
|
126
|
+
# any order. So, we just reuse header and then finalize the
|
127
|
+
# HttpResponse the right way. This will have no effect if called
|
128
|
+
# the second time if the first "outputted" anything.
|
129
|
+
def out(options = "text/html")
|
130
|
+
header(options)
|
131
|
+
@body.size == 0 or return
|
132
|
+
@body << yield if block_given?
|
133
|
+
end
|
134
|
+
|
135
|
+
# Used to wrap the normal stdinput variable used inside CGI.
|
136
|
+
def stdinput
|
137
|
+
@env_table[RACK_INPUT]
|
138
|
+
end
|
139
|
+
|
140
|
+
# return a pointer to the StringIO body since it's STDOUT-like
|
141
|
+
def stdoutput
|
142
|
+
@body
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|