edgar-rack 1.2.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/COPYING +18 -0
- data/KNOWN-ISSUES +21 -0
- data/README +401 -0
- data/Rakefile +101 -0
- data/SPEC +171 -0
- data/bin/rackup +4 -0
- data/contrib/rack_logo.svg +111 -0
- data/example/lobster.ru +4 -0
- data/example/protectedlobster.rb +14 -0
- data/example/protectedlobster.ru +8 -0
- data/lib/rack.rb +81 -0
- data/lib/rack/auth/abstract/handler.rb +37 -0
- data/lib/rack/auth/abstract/request.rb +43 -0
- data/lib/rack/auth/basic.rb +58 -0
- data/lib/rack/auth/digest/md5.rb +124 -0
- data/lib/rack/auth/digest/nonce.rb +51 -0
- data/lib/rack/auth/digest/params.rb +53 -0
- data/lib/rack/auth/digest/request.rb +40 -0
- data/lib/rack/builder.rb +80 -0
- data/lib/rack/cascade.rb +41 -0
- data/lib/rack/chunked.rb +52 -0
- data/lib/rack/commonlogger.rb +49 -0
- data/lib/rack/conditionalget.rb +63 -0
- data/lib/rack/config.rb +15 -0
- data/lib/rack/content_length.rb +29 -0
- data/lib/rack/content_type.rb +23 -0
- data/lib/rack/deflater.rb +96 -0
- data/lib/rack/directory.rb +157 -0
- data/lib/rack/etag.rb +59 -0
- data/lib/rack/file.rb +118 -0
- data/lib/rack/handler.rb +88 -0
- data/lib/rack/handler/cgi.rb +61 -0
- data/lib/rack/handler/evented_mongrel.rb +8 -0
- data/lib/rack/handler/fastcgi.rb +90 -0
- data/lib/rack/handler/lsws.rb +61 -0
- data/lib/rack/handler/mongrel.rb +90 -0
- data/lib/rack/handler/scgi.rb +59 -0
- data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
- data/lib/rack/handler/thin.rb +17 -0
- data/lib/rack/handler/webrick.rb +73 -0
- data/lib/rack/head.rb +19 -0
- data/lib/rack/lint.rb +567 -0
- data/lib/rack/lobster.rb +65 -0
- data/lib/rack/lock.rb +44 -0
- data/lib/rack/logger.rb +18 -0
- data/lib/rack/methodoverride.rb +27 -0
- data/lib/rack/mime.rb +210 -0
- data/lib/rack/mock.rb +185 -0
- data/lib/rack/nulllogger.rb +18 -0
- data/lib/rack/recursive.rb +61 -0
- data/lib/rack/reloader.rb +109 -0
- data/lib/rack/request.rb +307 -0
- data/lib/rack/response.rb +151 -0
- data/lib/rack/rewindable_input.rb +104 -0
- data/lib/rack/runtime.rb +27 -0
- data/lib/rack/sendfile.rb +139 -0
- data/lib/rack/server.rb +289 -0
- data/lib/rack/session/abstract/id.rb +348 -0
- data/lib/rack/session/cookie.rb +152 -0
- data/lib/rack/session/memcache.rb +93 -0
- data/lib/rack/session/pool.rb +79 -0
- data/lib/rack/showexceptions.rb +378 -0
- data/lib/rack/showstatus.rb +113 -0
- data/lib/rack/static.rb +53 -0
- data/lib/rack/urlmap.rb +55 -0
- data/lib/rack/utils.rb +698 -0
- data/rack.gemspec +39 -0
- data/test/cgi/lighttpd.conf +25 -0
- data/test/cgi/rackup_stub.rb +6 -0
- data/test/cgi/sample_rackup.ru +5 -0
- data/test/cgi/test +9 -0
- data/test/cgi/test.fcgi +8 -0
- data/test/cgi/test.ru +5 -0
- data/test/gemloader.rb +6 -0
- data/test/multipart/bad_robots +259 -0
- data/test/multipart/binary +0 -0
- data/test/multipart/empty +10 -0
- data/test/multipart/fail_16384_nofile +814 -0
- data/test/multipart/file1.txt +1 -0
- data/test/multipart/filename_and_modification_param +7 -0
- data/test/multipart/filename_with_escaped_quotes +6 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/test/multipart/filename_with_percent_escaped_quotes +6 -0
- data/test/multipart/filename_with_unescaped_quotes +6 -0
- data/test/multipart/ie +6 -0
- data/test/multipart/nested +10 -0
- data/test/multipart/none +9 -0
- data/test/multipart/semicolon +6 -0
- data/test/multipart/text +15 -0
- data/test/rackup/config.ru +31 -0
- data/test/spec_auth_basic.rb +70 -0
- data/test/spec_auth_digest.rb +241 -0
- data/test/spec_builder.rb +123 -0
- data/test/spec_cascade.rb +45 -0
- data/test/spec_cgi.rb +102 -0
- data/test/spec_chunked.rb +60 -0
- data/test/spec_commonlogger.rb +56 -0
- data/test/spec_conditionalget.rb +86 -0
- data/test/spec_config.rb +23 -0
- data/test/spec_content_length.rb +36 -0
- data/test/spec_content_type.rb +29 -0
- data/test/spec_deflater.rb +125 -0
- data/test/spec_directory.rb +57 -0
- data/test/spec_etag.rb +75 -0
- data/test/spec_fastcgi.rb +107 -0
- data/test/spec_file.rb +92 -0
- data/test/spec_handler.rb +49 -0
- data/test/spec_head.rb +30 -0
- data/test/spec_lint.rb +515 -0
- data/test/spec_lobster.rb +43 -0
- data/test/spec_lock.rb +142 -0
- data/test/spec_logger.rb +28 -0
- data/test/spec_methodoverride.rb +58 -0
- data/test/spec_mock.rb +241 -0
- data/test/spec_mongrel.rb +182 -0
- data/test/spec_nulllogger.rb +12 -0
- data/test/spec_recursive.rb +69 -0
- data/test/spec_request.rb +774 -0
- data/test/spec_response.rb +245 -0
- data/test/spec_rewindable_input.rb +118 -0
- data/test/spec_runtime.rb +39 -0
- data/test/spec_sendfile.rb +83 -0
- data/test/spec_server.rb +8 -0
- data/test/spec_session_abstract_id.rb +43 -0
- data/test/spec_session_cookie.rb +171 -0
- data/test/spec_session_memcache.rb +289 -0
- data/test/spec_session_pool.rb +200 -0
- data/test/spec_showexceptions.rb +87 -0
- data/test/spec_showstatus.rb +79 -0
- data/test/spec_static.rb +48 -0
- data/test/spec_thin.rb +86 -0
- data/test/spec_urlmap.rb +213 -0
- data/test/spec_utils.rb +678 -0
- data/test/spec_webrick.rb +141 -0
- data/test/testrequest.rb +78 -0
- data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +329 -0
data/lib/rack/etag.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'digest/md5'
|
|
2
|
+
|
|
3
|
+
module Rack
|
|
4
|
+
# Automatically sets the ETag header on all String bodies.
|
|
5
|
+
#
|
|
6
|
+
# The ETag header is skipped if ETag or Last-Modified headers are sent or if
|
|
7
|
+
# a sendfile body (body.responds_to :to_path) is given (since such cases
|
|
8
|
+
# should be handled by apache/nginx).
|
|
9
|
+
#
|
|
10
|
+
# On initialization, you can pass two parameters: a Cache-Control directive
|
|
11
|
+
# used when Etag is absent and a directive when it is present. The first
|
|
12
|
+
# defaults to nil, while the second defaults to "max-age=0, privaute, must-revalidate"
|
|
13
|
+
class ETag
|
|
14
|
+
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
|
|
15
|
+
|
|
16
|
+
def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTROL)
|
|
17
|
+
@app = app
|
|
18
|
+
@cache_control = cache_control
|
|
19
|
+
@no_cache_control = no_cache_control
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call(env)
|
|
23
|
+
status, headers, body = @app.call(env)
|
|
24
|
+
|
|
25
|
+
if etag_status?(status) && etag_body?(body) && !http_caching?(headers)
|
|
26
|
+
digest, body = digest_body(body)
|
|
27
|
+
headers['ETag'] = %("#{digest}") if digest
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
unless headers['Cache-Control']
|
|
31
|
+
headers['Cache-Control'] = digest ? @cache_control : @no_cache_control
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
[status, headers, body]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def etag_status?(status)
|
|
40
|
+
status == 200 || status == 201
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def etag_body?(body)
|
|
44
|
+
!body.respond_to?(:to_path)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def http_caching?(headers)
|
|
48
|
+
headers.key?('ETag') || headers.key?('Last-Modified')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def digest_body(body)
|
|
52
|
+
parts = []
|
|
53
|
+
body.each { |part| parts << part }
|
|
54
|
+
string_body = parts.join
|
|
55
|
+
digest = Digest::MD5.hexdigest(string_body) unless string_body.empty?
|
|
56
|
+
[digest, parts]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/rack/file.rb
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'rack/utils'
|
|
3
|
+
require 'rack/mime'
|
|
4
|
+
|
|
5
|
+
module Rack
|
|
6
|
+
# Rack::File serves files below the +root+ directory given, according to the
|
|
7
|
+
# path info of the Rack request.
|
|
8
|
+
# e.g. when Rack::File.new("/etc") is used, you can access 'passwd' file
|
|
9
|
+
# as http://localhost:9292/passwd
|
|
10
|
+
#
|
|
11
|
+
# Handlers can detect if bodies are a Rack::File, and use mechanisms
|
|
12
|
+
# like sendfile on the +path+.
|
|
13
|
+
|
|
14
|
+
class File
|
|
15
|
+
attr_accessor :root
|
|
16
|
+
attr_accessor :path
|
|
17
|
+
|
|
18
|
+
alias :to_path :path
|
|
19
|
+
|
|
20
|
+
def initialize(root)
|
|
21
|
+
@root = root
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(env)
|
|
25
|
+
dup._call(env)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
F = ::File
|
|
29
|
+
|
|
30
|
+
def _call(env)
|
|
31
|
+
@path_info = Utils.unescape(env["PATH_INFO"])
|
|
32
|
+
return fail(403, "Forbidden") if @path_info.include? ".."
|
|
33
|
+
|
|
34
|
+
@path = F.join(@root, @path_info)
|
|
35
|
+
|
|
36
|
+
available = begin
|
|
37
|
+
F.file?(@path) && F.readable?(@path)
|
|
38
|
+
rescue SystemCallError
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if available
|
|
43
|
+
serving(env)
|
|
44
|
+
else
|
|
45
|
+
fail(404, "File not found: #{@path_info}")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def serving(env)
|
|
50
|
+
# NOTE:
|
|
51
|
+
# We check via File::size? whether this file provides size info
|
|
52
|
+
# via stat (e.g. /proc files often don't), otherwise we have to
|
|
53
|
+
# figure it out by reading the whole file into memory.
|
|
54
|
+
size = F.size?(@path) || Utils.bytesize(F.read(@path))
|
|
55
|
+
|
|
56
|
+
response = [
|
|
57
|
+
200,
|
|
58
|
+
{
|
|
59
|
+
"Last-Modified" => F.mtime(@path).httpdate,
|
|
60
|
+
"Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain')
|
|
61
|
+
},
|
|
62
|
+
self
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
ranges = Rack::Utils.byte_ranges(env, size)
|
|
66
|
+
if ranges.nil? || ranges.length > 1
|
|
67
|
+
# No ranges, or multiple ranges (which we don't support):
|
|
68
|
+
# TODO: Support multiple byte-ranges
|
|
69
|
+
response[0] = 200
|
|
70
|
+
@range = 0..size-1
|
|
71
|
+
elsif ranges.empty?
|
|
72
|
+
# Unsatisfiable. Return error, and file size:
|
|
73
|
+
response = fail(416, "Byte range unsatisfiable")
|
|
74
|
+
response[1]["Content-Range"] = "bytes */#{size}"
|
|
75
|
+
return response
|
|
76
|
+
else
|
|
77
|
+
# Partial content:
|
|
78
|
+
@range = ranges[0]
|
|
79
|
+
response[0] = 206
|
|
80
|
+
response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}"
|
|
81
|
+
size = @range.end - @range.begin + 1
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
response[1]["Content-Length"] = size.to_s
|
|
85
|
+
response
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def each
|
|
89
|
+
F.open(@path, "rb") do |file|
|
|
90
|
+
file.seek(@range.begin)
|
|
91
|
+
remaining_len = @range.end-@range.begin+1
|
|
92
|
+
while remaining_len > 0
|
|
93
|
+
part = file.read([8192, remaining_len].min)
|
|
94
|
+
break unless part
|
|
95
|
+
remaining_len -= part.length
|
|
96
|
+
|
|
97
|
+
yield part
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def fail(status, body)
|
|
105
|
+
body += "\n"
|
|
106
|
+
[
|
|
107
|
+
status,
|
|
108
|
+
{
|
|
109
|
+
"Content-Type" => "text/plain",
|
|
110
|
+
"Content-Length" => body.size.to_s,
|
|
111
|
+
"X-Cascade" => "pass"
|
|
112
|
+
},
|
|
113
|
+
[body]
|
|
114
|
+
]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/rack/handler.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Rack
|
|
2
|
+
# *Handlers* connect web servers with Rack.
|
|
3
|
+
#
|
|
4
|
+
# Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI
|
|
5
|
+
# and LiteSpeed.
|
|
6
|
+
#
|
|
7
|
+
# Handlers usually are activated by calling <tt>MyHandler.run(myapp)</tt>.
|
|
8
|
+
# A second optional hash can be passed to include server-specific
|
|
9
|
+
# configuration.
|
|
10
|
+
module Handler
|
|
11
|
+
def self.get(server)
|
|
12
|
+
return unless server
|
|
13
|
+
server = server.to_s
|
|
14
|
+
|
|
15
|
+
if klass = @handlers[server]
|
|
16
|
+
obj = Object
|
|
17
|
+
klass.split("::").each { |x| obj = obj.const_get(x) }
|
|
18
|
+
obj
|
|
19
|
+
else
|
|
20
|
+
try_require('rack/handler', server)
|
|
21
|
+
const_get(server)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.default(options = {})
|
|
26
|
+
# Guess.
|
|
27
|
+
if ENV.include?("PHP_FCGI_CHILDREN")
|
|
28
|
+
# We already speak FastCGI
|
|
29
|
+
options.delete :File
|
|
30
|
+
options.delete :Port
|
|
31
|
+
|
|
32
|
+
Rack::Handler::FastCGI
|
|
33
|
+
elsif ENV.include?("REQUEST_METHOD")
|
|
34
|
+
Rack::Handler::CGI
|
|
35
|
+
else
|
|
36
|
+
begin
|
|
37
|
+
Rack::Handler::Mongrel
|
|
38
|
+
rescue LoadError
|
|
39
|
+
Rack::Handler::WEBrick
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Transforms server-name constants to their canonical form as filenames,
|
|
45
|
+
# then tries to require them but silences the LoadError if not found
|
|
46
|
+
#
|
|
47
|
+
# Naming convention:
|
|
48
|
+
#
|
|
49
|
+
# Foo # => 'foo'
|
|
50
|
+
# FooBar # => 'foo_bar.rb'
|
|
51
|
+
# FooBAR # => 'foobar.rb'
|
|
52
|
+
# FOObar # => 'foobar.rb'
|
|
53
|
+
# FOOBAR # => 'foobar.rb'
|
|
54
|
+
# FooBarBaz # => 'foo_bar_baz.rb'
|
|
55
|
+
def self.try_require(prefix, const_name)
|
|
56
|
+
file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }.
|
|
57
|
+
gsub(/[A-Z]+[^A-Z]/, '_\&').downcase
|
|
58
|
+
|
|
59
|
+
require(::File.join(prefix, file))
|
|
60
|
+
rescue LoadError
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.register(server, klass)
|
|
64
|
+
@handlers ||= {}
|
|
65
|
+
@handlers[server] = klass
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
autoload :CGI, "rack/handler/cgi"
|
|
69
|
+
autoload :FastCGI, "rack/handler/fastcgi"
|
|
70
|
+
autoload :Mongrel, "rack/handler/mongrel"
|
|
71
|
+
autoload :EventedMongrel, "rack/handler/evented_mongrel"
|
|
72
|
+
autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel"
|
|
73
|
+
autoload :WEBrick, "rack/handler/webrick"
|
|
74
|
+
autoload :LSWS, "rack/handler/lsws"
|
|
75
|
+
autoload :SCGI, "rack/handler/scgi"
|
|
76
|
+
autoload :Thin, "rack/handler/thin"
|
|
77
|
+
|
|
78
|
+
register 'cgi', 'Rack::Handler::CGI'
|
|
79
|
+
register 'fastcgi', 'Rack::Handler::FastCGI'
|
|
80
|
+
register 'mongrel', 'Rack::Handler::Mongrel'
|
|
81
|
+
register 'emongrel', 'Rack::Handler::EventedMongrel'
|
|
82
|
+
register 'smongrel', 'Rack::Handler::SwiftipliedMongrel'
|
|
83
|
+
register 'webrick', 'Rack::Handler::WEBrick'
|
|
84
|
+
register 'lsws', 'Rack::Handler::LSWS'
|
|
85
|
+
register 'scgi', 'Rack::Handler::SCGI'
|
|
86
|
+
register 'thin', 'Rack::Handler::Thin'
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'rack/content_length'
|
|
2
|
+
require 'rack/rewindable_input'
|
|
3
|
+
|
|
4
|
+
module Rack
|
|
5
|
+
module Handler
|
|
6
|
+
class CGI
|
|
7
|
+
def self.run(app, options=nil)
|
|
8
|
+
$stdin.binmode
|
|
9
|
+
serve app
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.serve(app)
|
|
13
|
+
env = ENV.to_hash
|
|
14
|
+
env.delete "HTTP_CONTENT_LENGTH"
|
|
15
|
+
|
|
16
|
+
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
|
17
|
+
|
|
18
|
+
env.update({"rack.version" => Rack::VERSION,
|
|
19
|
+
"rack.input" => Rack::RewindableInput.new($stdin),
|
|
20
|
+
"rack.errors" => $stderr,
|
|
21
|
+
|
|
22
|
+
"rack.multithread" => false,
|
|
23
|
+
"rack.multiprocess" => true,
|
|
24
|
+
"rack.run_once" => true,
|
|
25
|
+
|
|
26
|
+
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
env["QUERY_STRING"] ||= ""
|
|
30
|
+
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
|
31
|
+
env["REQUEST_PATH"] ||= "/"
|
|
32
|
+
|
|
33
|
+
status, headers, body = app.call(env)
|
|
34
|
+
begin
|
|
35
|
+
send_headers status, headers
|
|
36
|
+
send_body body
|
|
37
|
+
ensure
|
|
38
|
+
body.close if body.respond_to? :close
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.send_headers(status, headers)
|
|
43
|
+
$stdout.print "Status: #{status}\r\n"
|
|
44
|
+
headers.each { |k, vs|
|
|
45
|
+
vs.split("\n").each { |v|
|
|
46
|
+
$stdout.print "#{k}: #{v}\r\n"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
$stdout.print "\r\n"
|
|
50
|
+
$stdout.flush
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.send_body(body)
|
|
54
|
+
body.each { |part|
|
|
55
|
+
$stdout.print part
|
|
56
|
+
$stdout.flush
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'fcgi'
|
|
2
|
+
require 'socket'
|
|
3
|
+
require 'rack/content_length'
|
|
4
|
+
require 'rack/rewindable_input'
|
|
5
|
+
|
|
6
|
+
if defined? FCGI::Stream
|
|
7
|
+
class FCGI::Stream
|
|
8
|
+
alias _rack_read_without_buffer read
|
|
9
|
+
|
|
10
|
+
def read(n, buffer=nil)
|
|
11
|
+
buf = _rack_read_without_buffer n
|
|
12
|
+
buffer.replace(buf.to_s) if buffer
|
|
13
|
+
buf
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module Rack
|
|
19
|
+
module Handler
|
|
20
|
+
class FastCGI
|
|
21
|
+
def self.run(app, options={})
|
|
22
|
+
if options[:File]
|
|
23
|
+
STDIN.reopen(UNIXServer.new(options[:File]))
|
|
24
|
+
elsif options[:Port]
|
|
25
|
+
STDIN.reopen(TCPServer.new(options[:Host], options[:Port]))
|
|
26
|
+
end
|
|
27
|
+
FCGI.each { |request|
|
|
28
|
+
serve request, app
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.serve(request, app)
|
|
33
|
+
env = request.env
|
|
34
|
+
env.delete "HTTP_CONTENT_LENGTH"
|
|
35
|
+
|
|
36
|
+
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
|
37
|
+
|
|
38
|
+
rack_input = RewindableInput.new(request.in)
|
|
39
|
+
|
|
40
|
+
env.update({"rack.version" => Rack::VERSION,
|
|
41
|
+
"rack.input" => rack_input,
|
|
42
|
+
"rack.errors" => request.err,
|
|
43
|
+
|
|
44
|
+
"rack.multithread" => false,
|
|
45
|
+
"rack.multiprocess" => true,
|
|
46
|
+
"rack.run_once" => false,
|
|
47
|
+
|
|
48
|
+
"rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
env["QUERY_STRING"] ||= ""
|
|
52
|
+
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
|
53
|
+
env["REQUEST_PATH"] ||= "/"
|
|
54
|
+
env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == ""
|
|
55
|
+
env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == ""
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
status, headers, body = app.call(env)
|
|
59
|
+
begin
|
|
60
|
+
send_headers request.out, status, headers
|
|
61
|
+
send_body request.out, body
|
|
62
|
+
ensure
|
|
63
|
+
body.close if body.respond_to? :close
|
|
64
|
+
end
|
|
65
|
+
ensure
|
|
66
|
+
rack_input.close
|
|
67
|
+
request.finish
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.send_headers(out, status, headers)
|
|
72
|
+
out.print "Status: #{status}\r\n"
|
|
73
|
+
headers.each { |k, vs|
|
|
74
|
+
vs.split("\n").each { |v|
|
|
75
|
+
out.print "#{k}: #{v}\r\n"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
out.print "\r\n"
|
|
79
|
+
out.flush
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.send_body(out, body)
|
|
83
|
+
body.each { |part|
|
|
84
|
+
out.print part
|
|
85
|
+
out.flush
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'lsapi'
|
|
2
|
+
require 'rack/content_length'
|
|
3
|
+
require 'rack/rewindable_input'
|
|
4
|
+
|
|
5
|
+
module Rack
|
|
6
|
+
module Handler
|
|
7
|
+
class LSWS
|
|
8
|
+
def self.run(app, options=nil)
|
|
9
|
+
while LSAPI.accept != nil
|
|
10
|
+
serve app
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
def self.serve(app)
|
|
14
|
+
env = ENV.to_hash
|
|
15
|
+
env.delete "HTTP_CONTENT_LENGTH"
|
|
16
|
+
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
|
17
|
+
|
|
18
|
+
rack_input = RewindableInput.new($stdin.read.to_s)
|
|
19
|
+
|
|
20
|
+
env.update(
|
|
21
|
+
"rack.version" => Rack::VERSION,
|
|
22
|
+
"rack.input" => rack_input,
|
|
23
|
+
"rack.errors" => $stderr,
|
|
24
|
+
"rack.multithread" => false,
|
|
25
|
+
"rack.multiprocess" => true,
|
|
26
|
+
"rack.run_once" => false,
|
|
27
|
+
"rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
env["QUERY_STRING"] ||= ""
|
|
31
|
+
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
|
32
|
+
env["REQUEST_PATH"] ||= "/"
|
|
33
|
+
status, headers, body = app.call(env)
|
|
34
|
+
begin
|
|
35
|
+
send_headers status, headers
|
|
36
|
+
send_body body
|
|
37
|
+
ensure
|
|
38
|
+
body.close if body.respond_to? :close
|
|
39
|
+
end
|
|
40
|
+
ensure
|
|
41
|
+
rack_input.close
|
|
42
|
+
end
|
|
43
|
+
def self.send_headers(status, headers)
|
|
44
|
+
print "Status: #{status}\r\n"
|
|
45
|
+
headers.each { |k, vs|
|
|
46
|
+
vs.split("\n").each { |v|
|
|
47
|
+
print "#{k}: #{v}\r\n"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
print "\r\n"
|
|
51
|
+
STDOUT.flush
|
|
52
|
+
end
|
|
53
|
+
def self.send_body(body)
|
|
54
|
+
body.each { |part|
|
|
55
|
+
print part
|
|
56
|
+
STDOUT.flush
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|