edgar-rack 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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,65 @@
1
+ require 'zlib'
2
+
3
+ require 'rack/request'
4
+ require 'rack/response'
5
+
6
+ module Rack
7
+ # Paste has a Pony, Rack has a Lobster!
8
+ class Lobster
9
+ LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2
10
+ P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0
11
+ t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ
12
+ I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0])
13
+
14
+ LambdaLobster = lambda { |env|
15
+ if env["QUERY_STRING"].include?("flip")
16
+ lobster = LobsterString.split("\n").
17
+ map { |line| line.ljust(42).reverse }.
18
+ join("\n")
19
+ href = "?"
20
+ else
21
+ lobster = LobsterString
22
+ href = "?flip"
23
+ end
24
+
25
+ content = ["<title>Lobstericious!</title>",
26
+ "<pre>", lobster, "</pre>",
27
+ "<a href='#{href}'>flip!</a>"]
28
+ length = content.inject(0) { |a,e| a+e.size }.to_s
29
+ [200, {"Content-Type" => "text/html", "Content-Length" => length}, content]
30
+ }
31
+
32
+ def call(env)
33
+ req = Request.new(env)
34
+ if req.GET["flip"] == "left"
35
+ lobster = LobsterString.split("\n").
36
+ map { |line| line.ljust(42).reverse }.
37
+ join("\n")
38
+ href = "?flip=right"
39
+ elsif req.GET["flip"] == "crash"
40
+ raise "Lobster crashed"
41
+ else
42
+ lobster = LobsterString
43
+ href = "?flip=left"
44
+ end
45
+
46
+ res = Response.new
47
+ res.write "<title>Lobstericious!</title>"
48
+ res.write "<pre>"
49
+ res.write lobster
50
+ res.write "</pre>"
51
+ res.write "<p><a href='#{href}'>flip!</a></p>"
52
+ res.write "<p><a href='?flip=crash'>crash!</a></p>"
53
+ res.finish
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ if $0 == __FILE__
60
+ require 'rack'
61
+ require 'rack/showexceptions'
62
+ Rack::Handler::WEBrick.run \
63
+ Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)),
64
+ :Port => 9292
65
+ end
@@ -0,0 +1,44 @@
1
+ require 'thread'
2
+
3
+ module Rack
4
+ class Lock
5
+ class Proxy < Struct.new(:target, :mutex) # :nodoc:
6
+ def each
7
+ target.each { |x| yield x }
8
+ end
9
+
10
+ def close
11
+ target.close if target.respond_to?(:close)
12
+ ensure
13
+ mutex.unlock
14
+ end
15
+
16
+ def to_path
17
+ target.to_path
18
+ end
19
+
20
+ def respond_to?(sym)
21
+ sym.to_sym == :close || target.respond_to?(sym)
22
+ end
23
+ end
24
+
25
+ FLAG = 'rack.multithread'.freeze
26
+
27
+ def initialize(app, mutex = Mutex.new)
28
+ @app, @mutex = app, mutex
29
+ end
30
+
31
+ def call(env)
32
+ old, env[FLAG] = env[FLAG], false
33
+ @mutex.lock
34
+ response = @app.call(env)
35
+ response[2] = Proxy.new(response[2], @mutex)
36
+ response
37
+ rescue Exception
38
+ @mutex.unlock
39
+ raise
40
+ ensure
41
+ env[FLAG] = old
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ require 'logger'
2
+
3
+ module Rack
4
+ # Sets up rack.logger to write to rack.errors stream
5
+ class Logger
6
+ def initialize(app, level = ::Logger::INFO)
7
+ @app, @level = app, level
8
+ end
9
+
10
+ def call(env)
11
+ logger = ::Logger.new(env['rack.errors'])
12
+ logger.level = @level
13
+
14
+ env['rack.logger'] = logger
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ module Rack
2
+ class MethodOverride
3
+ HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
4
+
5
+ METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
6
+ HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ if env["REQUEST_METHOD"] == "POST"
14
+ req = Request.new(env)
15
+ method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
16
+ env[HTTP_METHOD_OVERRIDE_HEADER]
17
+ method = method.to_s.upcase
18
+ if HTTP_METHODS.include?(method)
19
+ env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
20
+ env["REQUEST_METHOD"] = method
21
+ end
22
+ end
23
+
24
+ @app.call(env)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,210 @@
1
+ module Rack
2
+ module Mime
3
+ # Returns String with mime type if found, otherwise use +fallback+.
4
+ # +ext+ should be filename extension in the '.ext' format that
5
+ # File.extname(file) returns.
6
+ # +fallback+ may be any object
7
+ #
8
+ # Also see the documentation for MIME_TYPES
9
+ #
10
+ # Usage:
11
+ # Rack::Mime.mime_type('.foo')
12
+ #
13
+ # This is a shortcut for:
14
+ # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
15
+
16
+ def mime_type(ext, fallback='application/octet-stream')
17
+ MIME_TYPES.fetch(ext.to_s.downcase, fallback)
18
+ end
19
+ module_function :mime_type
20
+
21
+ # List of most common mime-types, selected various sources
22
+ # according to their usefulness in a webserving scope for Ruby
23
+ # users.
24
+ #
25
+ # To amend this list with your local mime.types list you can use:
26
+ #
27
+ # require 'webrick/httputils'
28
+ # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types')
29
+ # Rack::Mime::MIME_TYPES.merge!(list)
30
+ #
31
+ # To add the list mongrel provides, use:
32
+ #
33
+ # require 'mongrel/handlers'
34
+ # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
35
+
36
+ MIME_TYPES = {
37
+ ".3gp" => "video/3gpp",
38
+ ".a" => "application/octet-stream",
39
+ ".ai" => "application/postscript",
40
+ ".aif" => "audio/x-aiff",
41
+ ".aiff" => "audio/x-aiff",
42
+ ".asc" => "application/pgp-signature",
43
+ ".asf" => "video/x-ms-asf",
44
+ ".asm" => "text/x-asm",
45
+ ".asx" => "video/x-ms-asf",
46
+ ".atom" => "application/atom+xml",
47
+ ".au" => "audio/basic",
48
+ ".avi" => "video/x-msvideo",
49
+ ".bat" => "application/x-msdownload",
50
+ ".bin" => "application/octet-stream",
51
+ ".bmp" => "image/bmp",
52
+ ".bz2" => "application/x-bzip2",
53
+ ".c" => "text/x-c",
54
+ ".cab" => "application/vnd.ms-cab-compressed",
55
+ ".cc" => "text/x-c",
56
+ ".chm" => "application/vnd.ms-htmlhelp",
57
+ ".class" => "application/octet-stream",
58
+ ".com" => "application/x-msdownload",
59
+ ".conf" => "text/plain",
60
+ ".cpp" => "text/x-c",
61
+ ".crt" => "application/x-x509-ca-cert",
62
+ ".css" => "text/css",
63
+ ".csv" => "text/csv",
64
+ ".cxx" => "text/x-c",
65
+ ".deb" => "application/x-debian-package",
66
+ ".der" => "application/x-x509-ca-cert",
67
+ ".diff" => "text/x-diff",
68
+ ".djv" => "image/vnd.djvu",
69
+ ".djvu" => "image/vnd.djvu",
70
+ ".dll" => "application/x-msdownload",
71
+ ".dmg" => "application/octet-stream",
72
+ ".doc" => "application/msword",
73
+ ".dot" => "application/msword",
74
+ ".dtd" => "application/xml-dtd",
75
+ ".dvi" => "application/x-dvi",
76
+ ".ear" => "application/java-archive",
77
+ ".eml" => "message/rfc822",
78
+ ".eps" => "application/postscript",
79
+ ".exe" => "application/x-msdownload",
80
+ ".f" => "text/x-fortran",
81
+ ".f77" => "text/x-fortran",
82
+ ".f90" => "text/x-fortran",
83
+ ".flv" => "video/x-flv",
84
+ ".for" => "text/x-fortran",
85
+ ".gem" => "application/octet-stream",
86
+ ".gemspec" => "text/x-script.ruby",
87
+ ".gif" => "image/gif",
88
+ ".gz" => "application/x-gzip",
89
+ ".h" => "text/x-c",
90
+ ".htc" => "text/x-component",
91
+ ".hh" => "text/x-c",
92
+ ".htm" => "text/html",
93
+ ".html" => "text/html",
94
+ ".ico" => "image/vnd.microsoft.icon",
95
+ ".ics" => "text/calendar",
96
+ ".ifb" => "text/calendar",
97
+ ".iso" => "application/octet-stream",
98
+ ".jar" => "application/java-archive",
99
+ ".java" => "text/x-java-source",
100
+ ".jnlp" => "application/x-java-jnlp-file",
101
+ ".jpeg" => "image/jpeg",
102
+ ".jpg" => "image/jpeg",
103
+ ".js" => "application/javascript",
104
+ ".json" => "application/json",
105
+ ".log" => "text/plain",
106
+ ".m3u" => "audio/x-mpegurl",
107
+ ".m4v" => "video/mp4",
108
+ ".man" => "text/troff",
109
+ ".manifest"=> "text/cache-manifest",
110
+ ".mathml" => "application/mathml+xml",
111
+ ".mbox" => "application/mbox",
112
+ ".mdoc" => "text/troff",
113
+ ".me" => "text/troff",
114
+ ".mid" => "audio/midi",
115
+ ".midi" => "audio/midi",
116
+ ".mime" => "message/rfc822",
117
+ ".mml" => "application/mathml+xml",
118
+ ".mng" => "video/x-mng",
119
+ ".mov" => "video/quicktime",
120
+ ".mp3" => "audio/mpeg",
121
+ ".mp4" => "video/mp4",
122
+ ".mp4v" => "video/mp4",
123
+ ".mpeg" => "video/mpeg",
124
+ ".mpg" => "video/mpeg",
125
+ ".ms" => "text/troff",
126
+ ".msi" => "application/x-msdownload",
127
+ ".odp" => "application/vnd.oasis.opendocument.presentation",
128
+ ".ods" => "application/vnd.oasis.opendocument.spreadsheet",
129
+ ".odt" => "application/vnd.oasis.opendocument.text",
130
+ ".ogg" => "application/ogg",
131
+ ".ogv" => "video/ogg",
132
+ ".p" => "text/x-pascal",
133
+ ".pas" => "text/x-pascal",
134
+ ".pbm" => "image/x-portable-bitmap",
135
+ ".pdf" => "application/pdf",
136
+ ".pem" => "application/x-x509-ca-cert",
137
+ ".pgm" => "image/x-portable-graymap",
138
+ ".pgp" => "application/pgp-encrypted",
139
+ ".pkg" => "application/octet-stream",
140
+ ".pl" => "text/x-script.perl",
141
+ ".pm" => "text/x-script.perl-module",
142
+ ".png" => "image/png",
143
+ ".pnm" => "image/x-portable-anymap",
144
+ ".ppm" => "image/x-portable-pixmap",
145
+ ".pps" => "application/vnd.ms-powerpoint",
146
+ ".ppt" => "application/vnd.ms-powerpoint",
147
+ ".ps" => "application/postscript",
148
+ ".psd" => "image/vnd.adobe.photoshop",
149
+ ".py" => "text/x-script.python",
150
+ ".qt" => "video/quicktime",
151
+ ".ra" => "audio/x-pn-realaudio",
152
+ ".rake" => "text/x-script.ruby",
153
+ ".ram" => "audio/x-pn-realaudio",
154
+ ".rar" => "application/x-rar-compressed",
155
+ ".rb" => "text/x-script.ruby",
156
+ ".rdf" => "application/rdf+xml",
157
+ ".roff" => "text/troff",
158
+ ".rpm" => "application/x-redhat-package-manager",
159
+ ".rss" => "application/rss+xml",
160
+ ".rtf" => "application/rtf",
161
+ ".ru" => "text/x-script.ruby",
162
+ ".s" => "text/x-asm",
163
+ ".sgm" => "text/sgml",
164
+ ".sgml" => "text/sgml",
165
+ ".sh" => "application/x-sh",
166
+ ".sig" => "application/pgp-signature",
167
+ ".snd" => "audio/basic",
168
+ ".so" => "application/octet-stream",
169
+ ".svg" => "image/svg+xml",
170
+ ".svgz" => "image/svg+xml",
171
+ ".swf" => "application/x-shockwave-flash",
172
+ ".t" => "text/troff",
173
+ ".tar" => "application/x-tar",
174
+ ".tbz" => "application/x-bzip-compressed-tar",
175
+ ".tcl" => "application/x-tcl",
176
+ ".tex" => "application/x-tex",
177
+ ".texi" => "application/x-texinfo",
178
+ ".texinfo" => "application/x-texinfo",
179
+ ".text" => "text/plain",
180
+ ".tif" => "image/tiff",
181
+ ".tiff" => "image/tiff",
182
+ ".torrent" => "application/x-bittorrent",
183
+ ".tr" => "text/troff",
184
+ ".ttf" => "application/octet-stream",
185
+ ".txt" => "text/plain",
186
+ ".vcf" => "text/x-vcard",
187
+ ".vcs" => "text/x-vcalendar",
188
+ ".vrml" => "model/vrml",
189
+ ".war" => "application/java-archive",
190
+ ".wav" => "audio/x-wav",
191
+ ".webm" => "video/webm",
192
+ ".wma" => "audio/x-ms-wma",
193
+ ".wmv" => "video/x-ms-wmv",
194
+ ".wmx" => "video/x-ms-wmx",
195
+ ".woff" => "application/octet-stream",
196
+ ".wrl" => "model/vrml",
197
+ ".wsdl" => "application/wsdl+xml",
198
+ ".xbm" => "image/x-xbitmap",
199
+ ".xhtml" => "application/xhtml+xml",
200
+ ".xls" => "application/vnd.ms-excel",
201
+ ".xml" => "application/xml",
202
+ ".xpm" => "image/x-xpixmap",
203
+ ".xsl" => "application/xml",
204
+ ".xslt" => "application/xslt+xml",
205
+ ".yaml" => "text/yaml",
206
+ ".yml" => "text/yaml",
207
+ ".zip" => "application/zip",
208
+ }
209
+ end
210
+ end
@@ -0,0 +1,185 @@
1
+ require 'uri'
2
+ require 'stringio'
3
+ require 'rack'
4
+ require 'rack/lint'
5
+ require 'rack/utils'
6
+ require 'rack/response'
7
+
8
+ module Rack
9
+ # Rack::MockRequest helps testing your Rack application without
10
+ # actually using HTTP.
11
+ #
12
+ # After performing a request on a URL with get/post/put/delete, it
13
+ # returns a MockResponse with useful helper methods for effective
14
+ # testing.
15
+ #
16
+ # You can pass a hash with additional configuration to the
17
+ # get/post/put/delete.
18
+ # <tt>:input</tt>:: A String or IO-like to be used as rack.input.
19
+ # <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
20
+ # <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
21
+
22
+ class MockRequest
23
+ class FatalWarning < RuntimeError
24
+ end
25
+
26
+ class FatalWarner
27
+ def puts(warning)
28
+ raise FatalWarning, warning
29
+ end
30
+
31
+ def write(warning)
32
+ raise FatalWarning, warning
33
+ end
34
+
35
+ def flush
36
+ end
37
+
38
+ def string
39
+ ""
40
+ end
41
+ end
42
+
43
+ DEFAULT_ENV = {
44
+ "rack.version" => Rack::VERSION,
45
+ "rack.input" => StringIO.new,
46
+ "rack.errors" => StringIO.new,
47
+ "rack.multithread" => true,
48
+ "rack.multiprocess" => true,
49
+ "rack.run_once" => false,
50
+ }
51
+
52
+ def initialize(app)
53
+ @app = app
54
+ end
55
+
56
+ def get(uri, opts={}) request("GET", uri, opts) end
57
+ def post(uri, opts={}) request("POST", uri, opts) end
58
+ def put(uri, opts={}) request("PUT", uri, opts) end
59
+ def delete(uri, opts={}) request("DELETE", uri, opts) end
60
+
61
+ def request(method="GET", uri="", opts={})
62
+ env = self.class.env_for(uri, opts.merge(:method => method))
63
+
64
+ if opts[:lint]
65
+ app = Rack::Lint.new(@app)
66
+ else
67
+ app = @app
68
+ end
69
+
70
+ errors = env["rack.errors"]
71
+ MockResponse.new(*(app.call(env) + [errors]))
72
+ end
73
+
74
+ # Return the Rack environment used for a request to +uri+.
75
+ def self.env_for(uri="", opts={})
76
+ uri = URI(uri)
77
+ uri.path = "/#{uri.path}" unless uri.path[0] == ?/
78
+
79
+ env = DEFAULT_ENV.dup
80
+
81
+ env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
82
+ env["SERVER_NAME"] = uri.host || "example.org"
83
+ env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
84
+ env["QUERY_STRING"] = uri.query.to_s
85
+ env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
86
+ env["rack.url_scheme"] = uri.scheme || "http"
87
+ env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
88
+
89
+ env["SCRIPT_NAME"] = opts[:script_name] || ""
90
+
91
+ if opts[:fatal]
92
+ env["rack.errors"] = FatalWarner.new
93
+ else
94
+ env["rack.errors"] = StringIO.new
95
+ end
96
+
97
+ if params = opts[:params]
98
+ if env["REQUEST_METHOD"] == "GET"
99
+ params = Utils.parse_nested_query(params) if params.is_a?(String)
100
+ params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
101
+ env["QUERY_STRING"] = Utils.build_nested_query(params)
102
+ elsif !opts.has_key?(:input)
103
+ opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
104
+ if params.is_a?(Hash)
105
+ if data = Utils::Multipart.build_multipart(params)
106
+ opts[:input] = data
107
+ opts["CONTENT_LENGTH"] ||= data.length.to_s
108
+ opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
109
+ else
110
+ opts[:input] = Utils.build_nested_query(params)
111
+ end
112
+ else
113
+ opts[:input] = params
114
+ end
115
+ end
116
+ end
117
+
118
+ empty_str = ""
119
+ empty_str.force_encoding("ASCII-8BIT") if empty_str.respond_to? :force_encoding
120
+ opts[:input] ||= empty_str
121
+ if String === opts[:input]
122
+ rack_input = StringIO.new(opts[:input])
123
+ else
124
+ rack_input = opts[:input]
125
+ end
126
+
127
+ rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
128
+ env['rack.input'] = rack_input
129
+
130
+ env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
131
+
132
+ opts.each { |field, value|
133
+ env[field] = value if String === field
134
+ }
135
+
136
+ env
137
+ end
138
+ end
139
+
140
+ # Rack::MockResponse provides useful helpers for testing your apps.
141
+ # Usually, you don't create the MockResponse on your own, but use
142
+ # MockRequest.
143
+
144
+ class MockResponse < Rack::Response
145
+ # Headers
146
+ attr_reader :original_headers
147
+
148
+ # Errors
149
+ attr_accessor :errors
150
+
151
+ def initialize(status, headers, body, errors=StringIO.new(""))
152
+ @original_headers = headers
153
+ @errors = errors.string if errors.respond_to?(:string)
154
+ @body_string = nil
155
+
156
+ super(body, status, headers)
157
+ end
158
+
159
+ def =~(other)
160
+ body =~ other
161
+ end
162
+
163
+ def match(other)
164
+ body.match other
165
+ end
166
+
167
+ def body
168
+ # FIXME: apparently users of MockResponse expect the return value of
169
+ # MockResponse#body to be a string. However, the real response object
170
+ # returns the body as a list.
171
+ #
172
+ # See spec_showstatus.rb:
173
+ #
174
+ # should "not replace existing messages" do
175
+ # ...
176
+ # res.body.should == "foo!"
177
+ # end
178
+ super.join
179
+ end
180
+
181
+ def empty?
182
+ [201, 204, 304].include? status
183
+ end
184
+ end
185
+ end