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.
Files changed (138) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +401 -0
  4. data/Rakefile +101 -0
  5. data/SPEC +171 -0
  6. data/bin/rackup +4 -0
  7. data/contrib/rack_logo.svg +111 -0
  8. data/example/lobster.ru +4 -0
  9. data/example/protectedlobster.rb +14 -0
  10. data/example/protectedlobster.ru +8 -0
  11. data/lib/rack.rb +81 -0
  12. data/lib/rack/auth/abstract/handler.rb +37 -0
  13. data/lib/rack/auth/abstract/request.rb +43 -0
  14. data/lib/rack/auth/basic.rb +58 -0
  15. data/lib/rack/auth/digest/md5.rb +124 -0
  16. data/lib/rack/auth/digest/nonce.rb +51 -0
  17. data/lib/rack/auth/digest/params.rb +53 -0
  18. data/lib/rack/auth/digest/request.rb +40 -0
  19. data/lib/rack/builder.rb +80 -0
  20. data/lib/rack/cascade.rb +41 -0
  21. data/lib/rack/chunked.rb +52 -0
  22. data/lib/rack/commonlogger.rb +49 -0
  23. data/lib/rack/conditionalget.rb +63 -0
  24. data/lib/rack/config.rb +15 -0
  25. data/lib/rack/content_length.rb +29 -0
  26. data/lib/rack/content_type.rb +23 -0
  27. data/lib/rack/deflater.rb +96 -0
  28. data/lib/rack/directory.rb +157 -0
  29. data/lib/rack/etag.rb +59 -0
  30. data/lib/rack/file.rb +118 -0
  31. data/lib/rack/handler.rb +88 -0
  32. data/lib/rack/handler/cgi.rb +61 -0
  33. data/lib/rack/handler/evented_mongrel.rb +8 -0
  34. data/lib/rack/handler/fastcgi.rb +90 -0
  35. data/lib/rack/handler/lsws.rb +61 -0
  36. data/lib/rack/handler/mongrel.rb +90 -0
  37. data/lib/rack/handler/scgi.rb +59 -0
  38. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  39. data/lib/rack/handler/thin.rb +17 -0
  40. data/lib/rack/handler/webrick.rb +73 -0
  41. data/lib/rack/head.rb +19 -0
  42. data/lib/rack/lint.rb +567 -0
  43. data/lib/rack/lobster.rb +65 -0
  44. data/lib/rack/lock.rb +44 -0
  45. data/lib/rack/logger.rb +18 -0
  46. data/lib/rack/methodoverride.rb +27 -0
  47. data/lib/rack/mime.rb +210 -0
  48. data/lib/rack/mock.rb +185 -0
  49. data/lib/rack/nulllogger.rb +18 -0
  50. data/lib/rack/recursive.rb +61 -0
  51. data/lib/rack/reloader.rb +109 -0
  52. data/lib/rack/request.rb +307 -0
  53. data/lib/rack/response.rb +151 -0
  54. data/lib/rack/rewindable_input.rb +104 -0
  55. data/lib/rack/runtime.rb +27 -0
  56. data/lib/rack/sendfile.rb +139 -0
  57. data/lib/rack/server.rb +289 -0
  58. data/lib/rack/session/abstract/id.rb +348 -0
  59. data/lib/rack/session/cookie.rb +152 -0
  60. data/lib/rack/session/memcache.rb +93 -0
  61. data/lib/rack/session/pool.rb +79 -0
  62. data/lib/rack/showexceptions.rb +378 -0
  63. data/lib/rack/showstatus.rb +113 -0
  64. data/lib/rack/static.rb +53 -0
  65. data/lib/rack/urlmap.rb +55 -0
  66. data/lib/rack/utils.rb +698 -0
  67. data/rack.gemspec +39 -0
  68. data/test/cgi/lighttpd.conf +25 -0
  69. data/test/cgi/rackup_stub.rb +6 -0
  70. data/test/cgi/sample_rackup.ru +5 -0
  71. data/test/cgi/test +9 -0
  72. data/test/cgi/test.fcgi +8 -0
  73. data/test/cgi/test.ru +5 -0
  74. data/test/gemloader.rb +6 -0
  75. data/test/multipart/bad_robots +259 -0
  76. data/test/multipart/binary +0 -0
  77. data/test/multipart/empty +10 -0
  78. data/test/multipart/fail_16384_nofile +814 -0
  79. data/test/multipart/file1.txt +1 -0
  80. data/test/multipart/filename_and_modification_param +7 -0
  81. data/test/multipart/filename_with_escaped_quotes +6 -0
  82. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  83. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  84. data/test/multipart/filename_with_unescaped_quotes +6 -0
  85. data/test/multipart/ie +6 -0
  86. data/test/multipart/nested +10 -0
  87. data/test/multipart/none +9 -0
  88. data/test/multipart/semicolon +6 -0
  89. data/test/multipart/text +15 -0
  90. data/test/rackup/config.ru +31 -0
  91. data/test/spec_auth_basic.rb +70 -0
  92. data/test/spec_auth_digest.rb +241 -0
  93. data/test/spec_builder.rb +123 -0
  94. data/test/spec_cascade.rb +45 -0
  95. data/test/spec_cgi.rb +102 -0
  96. data/test/spec_chunked.rb +60 -0
  97. data/test/spec_commonlogger.rb +56 -0
  98. data/test/spec_conditionalget.rb +86 -0
  99. data/test/spec_config.rb +23 -0
  100. data/test/spec_content_length.rb +36 -0
  101. data/test/spec_content_type.rb +29 -0
  102. data/test/spec_deflater.rb +125 -0
  103. data/test/spec_directory.rb +57 -0
  104. data/test/spec_etag.rb +75 -0
  105. data/test/spec_fastcgi.rb +107 -0
  106. data/test/spec_file.rb +92 -0
  107. data/test/spec_handler.rb +49 -0
  108. data/test/spec_head.rb +30 -0
  109. data/test/spec_lint.rb +515 -0
  110. data/test/spec_lobster.rb +43 -0
  111. data/test/spec_lock.rb +142 -0
  112. data/test/spec_logger.rb +28 -0
  113. data/test/spec_methodoverride.rb +58 -0
  114. data/test/spec_mock.rb +241 -0
  115. data/test/spec_mongrel.rb +182 -0
  116. data/test/spec_nulllogger.rb +12 -0
  117. data/test/spec_recursive.rb +69 -0
  118. data/test/spec_request.rb +774 -0
  119. data/test/spec_response.rb +245 -0
  120. data/test/spec_rewindable_input.rb +118 -0
  121. data/test/spec_runtime.rb +39 -0
  122. data/test/spec_sendfile.rb +83 -0
  123. data/test/spec_server.rb +8 -0
  124. data/test/spec_session_abstract_id.rb +43 -0
  125. data/test/spec_session_cookie.rb +171 -0
  126. data/test/spec_session_memcache.rb +289 -0
  127. data/test/spec_session_pool.rb +200 -0
  128. data/test/spec_showexceptions.rb +87 -0
  129. data/test/spec_showstatus.rb +79 -0
  130. data/test/spec_static.rb +48 -0
  131. data/test/spec_thin.rb +86 -0
  132. data/test/spec_urlmap.rb +213 -0
  133. data/test/spec_utils.rb +678 -0
  134. data/test/spec_webrick.rb +141 -0
  135. data/test/testrequest.rb +78 -0
  136. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  137. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  138. metadata +329 -0
@@ -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
@@ -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
@@ -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,8 @@
1
+ require 'swiftcore/evented_mongrel'
2
+
3
+ module Rack
4
+ module Handler
5
+ class EventedMongrel < Handler::Mongrel
6
+ end
7
+ end
8
+ 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