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,182 @@
1
+ begin
2
+ require 'rack'
3
+ require 'rack/handler/mongrel'
4
+ require File.expand_path('../testrequest', __FILE__)
5
+ require 'timeout'
6
+
7
+ Thread.abort_on_exception = true
8
+ $tcp_defer_accept_opts = nil
9
+ $tcp_cork_opts = nil
10
+
11
+ describe Rack::Handler::Mongrel do
12
+ extend TestRequest::Helpers
13
+
14
+ @server = Mongrel::HttpServer.new(@host='0.0.0.0', @port=9201)
15
+ @server.register('/test',
16
+ Rack::Handler::Mongrel.new(Rack::Lint.new(TestRequest.new)))
17
+ @server.register('/stream',
18
+ Rack::Handler::Mongrel.new(Rack::Lint.new(StreamingRequest)))
19
+ @acc = @server.run
20
+
21
+ should "respond" do
22
+ lambda {
23
+ GET("/test")
24
+ }.should.not.raise
25
+ end
26
+
27
+ should "be a Mongrel" do
28
+ GET("/test")
29
+ status.should.equal 200
30
+ response["SERVER_SOFTWARE"].should =~ /Mongrel/
31
+ response["HTTP_VERSION"].should.equal "HTTP/1.1"
32
+ response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
33
+ response["SERVER_PORT"].should.equal "9201"
34
+ response["SERVER_NAME"].should.equal "0.0.0.0"
35
+ end
36
+
37
+ should "have rack headers" do
38
+ GET("/test")
39
+ response["rack.version"].should.equal [1,1]
40
+ response["rack.multithread"].should.be.true
41
+ response["rack.multiprocess"].should.be.false
42
+ response["rack.run_once"].should.be.false
43
+ end
44
+
45
+ should "have CGI headers on GET" do
46
+ GET("/test")
47
+ response["REQUEST_METHOD"].should.equal "GET"
48
+ response["SCRIPT_NAME"].should.equal "/test"
49
+ response["REQUEST_PATH"].should.equal "/test"
50
+ response["PATH_INFO"].should.be.equal ""
51
+ response["QUERY_STRING"].should.equal ""
52
+ response["test.postdata"].should.equal ""
53
+
54
+ GET("/test/foo?quux=1")
55
+ response["REQUEST_METHOD"].should.equal "GET"
56
+ response["SCRIPT_NAME"].should.equal "/test"
57
+ response["REQUEST_PATH"].should.equal "/test/foo"
58
+ response["PATH_INFO"].should.equal "/foo"
59
+ response["QUERY_STRING"].should.equal "quux=1"
60
+ end
61
+
62
+ should "have CGI headers on POST" do
63
+ POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
64
+ status.should.equal 200
65
+ response["REQUEST_METHOD"].should.equal "POST"
66
+ response["SCRIPT_NAME"].should.equal "/test"
67
+ response["REQUEST_PATH"].should.equal "/test"
68
+ response["QUERY_STRING"].should.equal ""
69
+ response["HTTP_X_TEST_HEADER"].should.equal "42"
70
+ response["test.postdata"].should.equal "rack-form-data=23"
71
+ end
72
+
73
+ should "support HTTP auth" do
74
+ GET("/test", {:user => "ruth", :passwd => "secret"})
75
+ response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
76
+ end
77
+
78
+ should "set status" do
79
+ GET("/test?secret")
80
+ status.should.equal 403
81
+ response["rack.url_scheme"].should.equal "http"
82
+ end
83
+
84
+ should "provide a .run" do
85
+ block_ran = false
86
+ Thread.new {
87
+ Rack::Handler::Mongrel.run(lambda {}, {:Port => 9211}) { |server|
88
+ server.should.be.kind_of Mongrel::HttpServer
89
+ block_ran = true
90
+ }
91
+ }
92
+ sleep 1
93
+ block_ran.should.be.true
94
+ end
95
+
96
+ should "provide a .run that maps a hash" do
97
+ block_ran = false
98
+ Thread.new {
99
+ map = {'/'=>lambda{},'/foo'=>lambda{}}
100
+ Rack::Handler::Mongrel.run(map, :map => true, :Port => 9221) { |server|
101
+ server.should.be.kind_of Mongrel::HttpServer
102
+ server.classifier.uris.size.should.equal 2
103
+ server.classifier.uris.should.not.include '/arf'
104
+ server.classifier.uris.should.include '/'
105
+ server.classifier.uris.should.include '/foo'
106
+ block_ran = true
107
+ }
108
+ }
109
+ sleep 1
110
+ block_ran.should.be.true
111
+ end
112
+
113
+ should "provide a .run that maps a urlmap" do
114
+ block_ran = false
115
+ Thread.new {
116
+ map = Rack::URLMap.new({'/'=>lambda{},'/bar'=>lambda{}})
117
+ Rack::Handler::Mongrel.run(map, {:map => true, :Port => 9231}) { |server|
118
+ server.should.be.kind_of Mongrel::HttpServer
119
+ server.classifier.uris.size.should.equal 2
120
+ server.classifier.uris.should.not.include '/arf'
121
+ server.classifier.uris.should.include '/'
122
+ server.classifier.uris.should.include '/bar'
123
+ block_ran = true
124
+ }
125
+ }
126
+ sleep 1
127
+ block_ran.should.be.true
128
+ end
129
+
130
+ should "provide a .run that maps a urlmap restricting by host" do
131
+ block_ran = false
132
+ Thread.new {
133
+ map = Rack::URLMap.new({
134
+ '/' => lambda{},
135
+ '/foo' => lambda{},
136
+ '/bar' => lambda{},
137
+ 'http://localhost/' => lambda{},
138
+ 'http://localhost/bar' => lambda{},
139
+ 'http://falsehost/arf' => lambda{},
140
+ 'http://falsehost/qux' => lambda{}
141
+ })
142
+ opt = {:map => true, :Port => 9241, :Host => 'localhost'}
143
+ Rack::Handler::Mongrel.run(map, opt) { |server|
144
+ server.should.be.kind_of Mongrel::HttpServer
145
+ server.classifier.uris.should.include '/'
146
+ server.classifier.handler_map['/'].size.should.equal 2
147
+ server.classifier.uris.should.include '/foo'
148
+ server.classifier.handler_map['/foo'].size.should.equal 1
149
+ server.classifier.uris.should.include '/bar'
150
+ server.classifier.handler_map['/bar'].size.should.equal 2
151
+ server.classifier.uris.should.not.include '/qux'
152
+ server.classifier.uris.should.not.include '/arf'
153
+ server.classifier.uris.size.should.equal 3
154
+ block_ran = true
155
+ }
156
+ }
157
+ sleep 1
158
+ block_ran.should.be.true
159
+ end
160
+
161
+ should "stream #each part of the response" do
162
+ body = ''
163
+ begin
164
+ Timeout.timeout(1) do
165
+ Net::HTTP.start(@host, @port) do |http|
166
+ get = Net::HTTP::Get.new('/stream')
167
+ http.request(get) do |response|
168
+ response.read_body { |part| body << part }
169
+ end
170
+ end
171
+ end
172
+ rescue Timeout::Error
173
+ end
174
+ body.should.not.be.empty
175
+ end
176
+
177
+ @acc.raise Mongrel::StopServer
178
+ end
179
+
180
+ rescue LoadError
181
+ warn "Skipping Rack::Handler::Mongrel tests (Mongrel is required). `gem install mongrel` and try again."
182
+ end
@@ -0,0 +1,12 @@
1
+ require 'rack/nulllogger'
2
+
3
+ describe Rack::NullLogger do
4
+ should "act as a noop logger" do
5
+ app = lambda { |env|
6
+ env['rack.logger'].warn "b00m"
7
+ [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]]
8
+ }
9
+ logger = Rack::NullLogger.new(app)
10
+ lambda{ logger.call({}) }.should.not.raise
11
+ end
12
+ end
@@ -0,0 +1,69 @@
1
+ require 'rack/recursive'
2
+ require 'rack/mock'
3
+
4
+ describe Rack::Recursive do
5
+ @app1 = lambda { |env|
6
+ res = Rack::Response.new
7
+ res["X-Path-Info"] = env["PATH_INFO"]
8
+ res["X-Query-String"] = env["QUERY_STRING"]
9
+ res.finish do |inner_res|
10
+ inner_res.write "App1"
11
+ end
12
+ }
13
+
14
+ @app2 = lambda { |env|
15
+ Rack::Response.new.finish do |res|
16
+ res.write "App2"
17
+ _, _, body = env['rack.recursive.include'].call(env, "/app1")
18
+ body.each { |b|
19
+ res.write b
20
+ }
21
+ end
22
+ }
23
+
24
+ @app3 = lambda { |env|
25
+ raise Rack::ForwardRequest.new("/app1")
26
+ }
27
+
28
+ @app4 = lambda { |env|
29
+ raise Rack::ForwardRequest.new("http://example.org/app1/quux?meh")
30
+ }
31
+
32
+ should "allow for subrequests" do
33
+ res = Rack::MockRequest.new(Rack::Recursive.new(
34
+ Rack::URLMap.new("/app1" => @app1,
35
+ "/app2" => @app2))).
36
+ get("/app2")
37
+
38
+ res.should.be.ok
39
+ res.body.should.equal "App2App1"
40
+ end
41
+
42
+ should "raise error on requests not below the app" do
43
+ app = Rack::URLMap.new("/app1" => @app1,
44
+ "/app" => Rack::Recursive.new(
45
+ Rack::URLMap.new("/1" => @app1,
46
+ "/2" => @app2)))
47
+
48
+ lambda {
49
+ Rack::MockRequest.new(app).get("/app/2")
50
+ }.should.raise(ArgumentError).
51
+ message.should =~ /can only include below/
52
+ end
53
+
54
+ should "support forwarding" do
55
+ app = Rack::Recursive.new(Rack::URLMap.new("/app1" => @app1,
56
+ "/app3" => @app3,
57
+ "/app4" => @app4))
58
+
59
+ res = Rack::MockRequest.new(app).get("/app3")
60
+ res.should.be.ok
61
+ res.body.should.equal "App1"
62
+
63
+ res = Rack::MockRequest.new(app).get("/app4")
64
+ res.should.be.ok
65
+ res.body.should.equal "App1"
66
+ res["X-Path-Info"].should.equal "/quux"
67
+ res["X-Query-String"].should.equal "meh"
68
+ end
69
+ end
@@ -0,0 +1,774 @@
1
+ require 'stringio'
2
+ require 'cgi'
3
+ require 'rack/request'
4
+ require 'rack/mock'
5
+
6
+ describe Rack::Request do
7
+ should "wrap the rack variables" do
8
+ req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
9
+
10
+ req.body.should.respond_to? :gets
11
+ req.scheme.should.equal "http"
12
+ req.request_method.should.equal "GET"
13
+
14
+ req.should.be.get
15
+ req.should.not.be.post
16
+ req.should.not.be.put
17
+ req.should.not.be.delete
18
+ req.should.not.be.head
19
+
20
+ req.script_name.should.equal ""
21
+ req.path_info.should.equal "/"
22
+ req.query_string.should.equal ""
23
+
24
+ req.host.should.equal "example.com"
25
+ req.port.should.equal 8080
26
+
27
+ req.content_length.should.equal "0"
28
+ req.content_type.should.be.nil
29
+ end
30
+
31
+ should "figure out the correct host" do
32
+ req = Rack::Request.new \
33
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
34
+ req.host.should.equal "www2.example.org"
35
+
36
+ req = Rack::Request.new \
37
+ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
38
+ req.host.should.equal "example.org"
39
+
40
+ req = Rack::Request.new \
41
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
42
+ req.host.should.equal "example.org"
43
+
44
+ env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292")
45
+ env.delete("SERVER_NAME")
46
+ req = Rack::Request.new(env)
47
+ req.host.should.equal "192.168.1.1"
48
+
49
+ env = Rack::MockRequest.env_for("/")
50
+ env.delete("SERVER_NAME")
51
+ req = Rack::Request.new(env)
52
+ req.host.should.equal ""
53
+ end
54
+
55
+ should "figure out the correct port" do
56
+ req = Rack::Request.new \
57
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
58
+ req.port.should.equal 80
59
+
60
+ req = Rack::Request.new \
61
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org:81")
62
+ req.port.should.equal 81
63
+
64
+ req = Rack::Request.new \
65
+ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
66
+ req.port.should.equal 9292
67
+
68
+ req = Rack::Request.new \
69
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
70
+ req.port.should.equal 9292
71
+
72
+ req = Rack::Request.new \
73
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org")
74
+ req.port.should.equal 80
75
+
76
+ req = Rack::Request.new \
77
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_SSL" => "on")
78
+ req.port.should.equal 443
79
+
80
+ req = Rack::Request.new \
81
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PROTO" => "https")
82
+ req.port.should.equal 443
83
+
84
+ req = Rack::Request.new \
85
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PORT" => "9393")
86
+ req.port.should.equal 9393
87
+
88
+ req = Rack::Request.new \
89
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9393", "SERVER_PORT" => "80")
90
+ req.port.should.equal 9393
91
+
92
+ req = Rack::Request.new \
93
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
94
+ req.port.should.equal 80
95
+ end
96
+
97
+ should "figure out the correct host with port" do
98
+ req = Rack::Request.new \
99
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
100
+ req.host_with_port.should.equal "www2.example.org"
101
+
102
+ req = Rack::Request.new \
103
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81")
104
+ req.host_with_port.should.equal "localhost:81"
105
+
106
+ req = Rack::Request.new \
107
+ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
108
+ req.host_with_port.should.equal "example.org:9292"
109
+
110
+ req = Rack::Request.new \
111
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
112
+ req.host_with_port.should.equal "example.org:9292"
113
+
114
+ req = Rack::Request.new \
115
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
116
+ req.host_with_port.should.equal "example.org"
117
+ end
118
+
119
+ should "parse the query string" do
120
+ req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
121
+ req.query_string.should.equal "foo=bar&quux=bla"
122
+ req.GET.should.equal "foo" => "bar", "quux" => "bla"
123
+ req.POST.should.be.empty
124
+ req.params.should.equal "foo" => "bar", "quux" => "bla"
125
+ end
126
+
127
+ should "raise if rack.input is missing" do
128
+ req = Rack::Request.new({})
129
+ lambda { req.POST }.should.raise(RuntimeError)
130
+ end
131
+
132
+ should "parse POST data when method is POST and no Content-Type given" do
133
+ req = Rack::Request.new \
134
+ Rack::MockRequest.env_for("/?foo=quux",
135
+ "REQUEST_METHOD" => 'POST',
136
+ :input => "foo=bar&quux=bla")
137
+ req.content_type.should.be.nil
138
+ req.media_type.should.be.nil
139
+ req.query_string.should.equal "foo=quux"
140
+ req.GET.should.equal "foo" => "quux"
141
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
142
+ req.params.should.equal "foo" => "bar", "quux" => "bla"
143
+ end
144
+
145
+ should "parse POST data with explicit content type regardless of method" do
146
+ req = Rack::Request.new \
147
+ Rack::MockRequest.env_for("/",
148
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
149
+ :input => "foo=bar&quux=bla")
150
+ req.content_type.should.equal 'application/x-www-form-urlencoded;foo=bar'
151
+ req.media_type.should.equal 'application/x-www-form-urlencoded'
152
+ req.media_type_params['foo'].should.equal 'bar'
153
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
154
+ req.params.should.equal "foo" => "bar", "quux" => "bla"
155
+ end
156
+
157
+ should "not parse POST data when media type is not form-data" do
158
+ req = Rack::Request.new \
159
+ Rack::MockRequest.env_for("/?foo=quux",
160
+ "REQUEST_METHOD" => 'POST',
161
+ "CONTENT_TYPE" => 'text/plain;charset=utf-8',
162
+ :input => "foo=bar&quux=bla")
163
+ req.content_type.should.equal 'text/plain;charset=utf-8'
164
+ req.media_type.should.equal 'text/plain'
165
+ req.media_type_params['charset'].should.equal 'utf-8'
166
+ req.POST.should.be.empty
167
+ req.params.should.equal "foo" => "quux"
168
+ req.body.read.should.equal "foo=bar&quux=bla"
169
+ end
170
+
171
+ should "parse POST data on PUT when media type is form-data" do
172
+ req = Rack::Request.new \
173
+ Rack::MockRequest.env_for("/?foo=quux",
174
+ "REQUEST_METHOD" => 'PUT',
175
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded',
176
+ :input => "foo=bar&quux=bla")
177
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
178
+ req.body.read.should.equal "foo=bar&quux=bla"
179
+ end
180
+
181
+ should "rewind input after parsing POST data" do
182
+ input = StringIO.new("foo=bar&quux=bla")
183
+ req = Rack::Request.new \
184
+ Rack::MockRequest.env_for("/",
185
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
186
+ :input => input)
187
+ req.params.should.equal "foo" => "bar", "quux" => "bla"
188
+ input.read.should.equal "foo=bar&quux=bla"
189
+ end
190
+
191
+ should "clean up Safari's ajax POST body" do
192
+ req = Rack::Request.new \
193
+ Rack::MockRequest.env_for("/",
194
+ 'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0")
195
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
196
+ end
197
+
198
+ should "get value by key from params with #[]" do
199
+ req = Rack::Request.new \
200
+ Rack::MockRequest.env_for("?foo=quux")
201
+ req['foo'].should.equal 'quux'
202
+ req[:foo].should.equal 'quux'
203
+ end
204
+
205
+ should "set value to key on params with #[]=" do
206
+ req = Rack::Request.new \
207
+ Rack::MockRequest.env_for("?foo=duh")
208
+ req['foo'].should.equal 'duh'
209
+ req[:foo].should.equal 'duh'
210
+ req.params.should.equal 'foo' => 'duh'
211
+
212
+ req['foo'] = 'bar'
213
+ req.params.should.equal 'foo' => 'bar'
214
+ req['foo'].should.equal 'bar'
215
+ req[:foo].should.equal 'bar'
216
+
217
+ req[:foo] = 'jaz'
218
+ req.params.should.equal 'foo' => 'jaz'
219
+ req['foo'].should.equal 'jaz'
220
+ req[:foo].should.equal 'jaz'
221
+ end
222
+
223
+ should "return values for the keys in the order given from values_at" do
224
+ req = Rack::Request.new \
225
+ Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful")
226
+ req.values_at('foo').should.equal ['baz']
227
+ req.values_at('foo', 'wun').should.equal ['baz', 'der']
228
+ req.values_at('bar', 'foo', 'wun').should.equal ['ful', 'baz', 'der']
229
+ end
230
+
231
+ should "extract referrer correctly" do
232
+ req = Rack::Request.new \
233
+ Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path")
234
+ req.referer.should.equal "/some/path"
235
+
236
+ req = Rack::Request.new \
237
+ Rack::MockRequest.env_for("/")
238
+ req.referer.should.equal nil
239
+ end
240
+
241
+ should "extract user agent correctly" do
242
+ req = Rack::Request.new \
243
+ Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)")
244
+ req.user_agent.should.equal "Mozilla/4.0 (compatible)"
245
+
246
+ req = Rack::Request.new \
247
+ Rack::MockRequest.env_for("/")
248
+ req.user_agent.should.equal nil
249
+ end
250
+
251
+ should "treat missing content type as nil" do
252
+ req = Rack::Request.new \
253
+ Rack::MockRequest.env_for("/")
254
+ req.content_type.should.equal nil
255
+ end
256
+
257
+ should "treat empty content type as nil" do
258
+ req = Rack::Request.new \
259
+ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
260
+ req.content_type.should.equal nil
261
+ end
262
+
263
+ should "return nil media type for empty content type" do
264
+ req = Rack::Request.new \
265
+ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
266
+ req.media_type.should.equal nil
267
+ end
268
+
269
+ should "cache, but invalidates the cache" do
270
+ req = Rack::Request.new \
271
+ Rack::MockRequest.env_for("/?foo=quux",
272
+ "CONTENT_TYPE" => "application/x-www-form-urlencoded",
273
+ :input => "foo=bar&quux=bla")
274
+ req.GET.should.equal "foo" => "quux"
275
+ req.GET.should.equal "foo" => "quux"
276
+ req.env["QUERY_STRING"] = "bla=foo"
277
+ req.GET.should.equal "bla" => "foo"
278
+ req.GET.should.equal "bla" => "foo"
279
+
280
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
281
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
282
+ req.env["rack.input"] = StringIO.new("foo=bla&quux=bar")
283
+ req.POST.should.equal "foo" => "bla", "quux" => "bar"
284
+ req.POST.should.equal "foo" => "bla", "quux" => "bar"
285
+ end
286
+
287
+ should "figure out if called via XHR" do
288
+ req = Rack::Request.new(Rack::MockRequest.env_for(""))
289
+ req.should.not.be.xhr
290
+
291
+ req = Rack::Request.new \
292
+ Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
293
+ req.should.be.xhr
294
+ end
295
+
296
+ should "ssl detection" do
297
+ request = Rack::Request.new(Rack::MockRequest.env_for("/"))
298
+ request.scheme.should.equal "http"
299
+ request.should.not.be.ssl?
300
+
301
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTPS' => 'on'))
302
+ request.scheme.should.equal "https"
303
+ request.should.be.ssl?
304
+
305
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https'))
306
+ request.scheme.should.equal "https"
307
+ request.should.be.ssl?
308
+
309
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080'))
310
+ request.scheme.should.equal "http"
311
+ request.should.not.be.ssl?
312
+
313
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on'))
314
+ request.scheme.should.equal "https"
315
+ request.should.be.ssl?
316
+
317
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on'))
318
+ request.scheme.should.equal "https"
319
+ request.should.be.ssl?
320
+
321
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https'))
322
+ request.scheme.should.equal "https"
323
+ request.should.be.ssl?
324
+
325
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http'))
326
+ request.scheme.should.equal "https"
327
+ request.should.be.ssl
328
+ end
329
+
330
+ should "parse cookies" do
331
+ req = Rack::Request.new \
332
+ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
333
+ req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
334
+ req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
335
+ req.env.delete("HTTP_COOKIE")
336
+ req.cookies.should.equal({})
337
+ end
338
+
339
+ should "parse cookies according to RFC 2109" do
340
+ req = Rack::Request.new \
341
+ Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
342
+ req.cookies.should.equal 'foo' => 'bar'
343
+ end
344
+
345
+ should 'parse cookies with quotes' do
346
+ req = Rack::Request.new Rack::MockRequest.env_for('', {
347
+ 'HTTP_COOKIE' => '$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"'
348
+ })
349
+ req.cookies.should.equal({
350
+ '$Version' => '"1"',
351
+ 'Customer' => '"WILE_E_COYOTE"',
352
+ '$Path' => '"/acme"',
353
+ 'Part_Number' => '"Rocket_Launcher_0001"',
354
+ })
355
+ end
356
+
357
+ should "provide setters" do
358
+ req = Rack::Request.new(e=Rack::MockRequest.env_for(""))
359
+ req.script_name.should.equal ""
360
+ req.script_name = "/foo"
361
+ req.script_name.should.equal "/foo"
362
+ e["SCRIPT_NAME"].should.equal "/foo"
363
+
364
+ req.path_info.should.equal "/"
365
+ req.path_info = "/foo"
366
+ req.path_info.should.equal "/foo"
367
+ e["PATH_INFO"].should.equal "/foo"
368
+ end
369
+
370
+ should "provide the original env" do
371
+ req = Rack::Request.new(e = Rack::MockRequest.env_for(""))
372
+ req.env.should == e
373
+ end
374
+
375
+ should "restore the base URL" do
376
+ Rack::Request.new(Rack::MockRequest.env_for("")).base_url.
377
+ should.equal "http://example.org"
378
+ Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url.
379
+ should.equal "http://example.org"
380
+ end
381
+
382
+ should "restore the URL" do
383
+ Rack::Request.new(Rack::MockRequest.env_for("")).url.
384
+ should.equal "http://example.org/"
385
+ Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
386
+ should.equal "http://example.org/foo/"
387
+ Rack::Request.new(Rack::MockRequest.env_for("/foo")).url.
388
+ should.equal "http://example.org/foo"
389
+ Rack::Request.new(Rack::MockRequest.env_for("?foo")).url.
390
+ should.equal "http://example.org/?foo"
391
+ Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).url.
392
+ should.equal "http://example.org:8080/"
393
+ Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).url.
394
+ should.equal "https://example.org/"
395
+
396
+ Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url.
397
+ should.equal "https://example.com:8080/foo?foo"
398
+ end
399
+
400
+ should "restore the full path" do
401
+ Rack::Request.new(Rack::MockRequest.env_for("")).fullpath.
402
+ should.equal "/"
403
+ Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
404
+ should.equal "/foo/"
405
+ Rack::Request.new(Rack::MockRequest.env_for("/foo")).fullpath.
406
+ should.equal "/foo"
407
+ Rack::Request.new(Rack::MockRequest.env_for("?foo")).fullpath.
408
+ should.equal "/?foo"
409
+ Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath.
410
+ should.equal "/"
411
+ Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).fullpath.
412
+ should.equal "/"
413
+
414
+ Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath.
415
+ should.equal "/foo?foo"
416
+ end
417
+
418
+ should "handle multiple media type parameters" do
419
+ req = Rack::Request.new \
420
+ Rack::MockRequest.env_for("/",
421
+ "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam')
422
+ req.should.not.be.form_data
423
+ req.media_type_params.should.include 'foo'
424
+ req.media_type_params['foo'].should.equal 'BAR'
425
+ req.media_type_params.should.include 'baz'
426
+ req.media_type_params['baz'].should.equal 'bizzle dizzle'
427
+ req.media_type_params.should.not.include 'BLING'
428
+ req.media_type_params.should.include 'bling'
429
+ req.media_type_params['bling'].should.equal 'bam'
430
+ end
431
+
432
+ should "parse with junk before boundry" do
433
+ # Adapted from RFC 1867.
434
+ input = <<EOF
435
+ blah blah\r
436
+ \r
437
+ --AaB03x\r
438
+ content-disposition: form-data; name="reply"\r
439
+ \r
440
+ yes\r
441
+ --AaB03x\r
442
+ content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
443
+ Content-Type: image/jpeg\r
444
+ Content-Transfer-Encoding: base64\r
445
+ \r
446
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
447
+ --AaB03x--\r
448
+ EOF
449
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
450
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
451
+ "CONTENT_LENGTH" => input.size,
452
+ :input => input)
453
+
454
+ req.POST.should.include "fileupload"
455
+ req.POST.should.include "reply"
456
+
457
+ req.should.be.form_data
458
+ req.content_length.should.equal input.size
459
+ req.media_type.should.equal 'multipart/form-data'
460
+ req.media_type_params.should.include 'boundary'
461
+ req.media_type_params['boundary'].should.equal 'AaB03x'
462
+
463
+ req.POST["reply"].should.equal "yes"
464
+
465
+ f = req.POST["fileupload"]
466
+ f.should.be.kind_of Hash
467
+ f[:type].should.equal "image/jpeg"
468
+ f[:filename].should.equal "dj.jpg"
469
+ f.should.include :tempfile
470
+ f[:tempfile].size.should.equal 76
471
+ end
472
+
473
+ should "parse multipart form data" do
474
+ # Adapted from RFC 1867.
475
+ input = <<EOF
476
+ --AaB03x\r
477
+ content-disposition: form-data; name="reply"\r
478
+ \r
479
+ yes\r
480
+ --AaB03x\r
481
+ content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
482
+ Content-Type: image/jpeg\r
483
+ Content-Transfer-Encoding: base64\r
484
+ \r
485
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
486
+ --AaB03x--\r
487
+ EOF
488
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
489
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
490
+ "CONTENT_LENGTH" => input.size,
491
+ :input => input)
492
+
493
+ req.POST.should.include "fileupload"
494
+ req.POST.should.include "reply"
495
+
496
+ req.should.be.form_data
497
+ req.content_length.should.equal input.size
498
+ req.media_type.should.equal 'multipart/form-data'
499
+ req.media_type_params.should.include 'boundary'
500
+ req.media_type_params['boundary'].should.equal 'AaB03x'
501
+
502
+ req.POST["reply"].should.equal "yes"
503
+
504
+ f = req.POST["fileupload"]
505
+ f.should.be.kind_of Hash
506
+ f[:type].should.equal "image/jpeg"
507
+ f[:filename].should.equal "dj.jpg"
508
+ f.should.include :tempfile
509
+ f[:tempfile].size.should.equal 76
510
+ end
511
+
512
+ should "parse big multipart form data" do
513
+ input = <<EOF
514
+ --AaB03x\r
515
+ content-disposition: form-data; name="huge"; filename="huge"\r
516
+ \r
517
+ #{"x"*32768}\r
518
+ --AaB03x\r
519
+ content-disposition: form-data; name="mean"; filename="mean"\r
520
+ \r
521
+ --AaB03xha\r
522
+ --AaB03x--\r
523
+ EOF
524
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
525
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
526
+ "CONTENT_LENGTH" => input.size,
527
+ :input => input)
528
+
529
+ req.POST["huge"][:tempfile].size.should.equal 32768
530
+ req.POST["mean"][:tempfile].size.should.equal 10
531
+ req.POST["mean"][:tempfile].read.should.equal "--AaB03xha"
532
+ end
533
+
534
+ should "detect invalid multipart form data" do
535
+ input = <<EOF
536
+ --AaB03x\r
537
+ content-disposition: form-data; name="huge"; filename="huge"\r
538
+ EOF
539
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
540
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
541
+ "CONTENT_LENGTH" => input.size,
542
+ :input => input)
543
+
544
+ lambda { req.POST }.should.raise(EOFError)
545
+
546
+ input = <<EOF
547
+ --AaB03x\r
548
+ content-disposition: form-data; name="huge"; filename="huge"\r
549
+ \r
550
+ foo\r
551
+ EOF
552
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
553
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
554
+ "CONTENT_LENGTH" => input.size,
555
+ :input => input)
556
+
557
+ lambda { req.POST }.should.raise(EOFError)
558
+
559
+ input = <<EOF
560
+ --AaB03x\r
561
+ content-disposition: form-data; name="huge"; filename="huge"\r
562
+ \r
563
+ foo\r
564
+ EOF
565
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
566
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
567
+ "CONTENT_LENGTH" => input.size,
568
+ :input => input)
569
+
570
+ lambda { req.POST }.should.raise(EOFError)
571
+ end
572
+
573
+ should "correctly parse the part name from Content-Id header" do
574
+ input = <<EOF
575
+ --AaB03x\r
576
+ Content-Type: text/xml; charset=utf-8\r
577
+ Content-Id: <soap-start>\r
578
+ Content-Transfer-Encoding: 7bit\r
579
+ \r
580
+ foo\r
581
+ --AaB03x--\r
582
+ EOF
583
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
584
+ "CONTENT_TYPE" => "multipart/related, boundary=AaB03x",
585
+ "CONTENT_LENGTH" => input.size,
586
+ :input => input)
587
+
588
+ req.params.keys.should.equal ["<soap-start>"]
589
+ end
590
+
591
+ should "not try to interpret binary as utf8" do
592
+ if /regexp/.respond_to?(:kcode) # < 1.9
593
+ begin
594
+ original_kcode = $KCODE
595
+ $KCODE='UTF8'
596
+
597
+ input = <<EOF
598
+ --AaB03x\r
599
+ content-disposition: form-data; name="fileupload"; filename="junk.a"\r
600
+ content-type: application/octet-stream\r
601
+ \r
602
+ #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
603
+ --AaB03x--\r
604
+ EOF
605
+
606
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
607
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
608
+ "CONTENT_LENGTH" => input.size,
609
+ :input => input)
610
+
611
+ lambda{req.POST}.should.not.raise(EOFError)
612
+ req.POST["fileupload"][:tempfile].size.should.equal 4
613
+ ensure
614
+ $KCODE = original_kcode
615
+ end
616
+ else # >= 1.9
617
+ input = <<EOF
618
+ --AaB03x\r
619
+ content-disposition: form-data; name="fileupload"; filename="junk.a"\r
620
+ content-type: application/octet-stream\r
621
+ \r
622
+ #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
623
+ --AaB03x--\r
624
+ EOF
625
+
626
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
627
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
628
+ "CONTENT_LENGTH" => input.size,
629
+ :input => input)
630
+
631
+ lambda{req.POST}.should.not.raise(EOFError)
632
+ req.POST["fileupload"][:tempfile].size.should.equal 4
633
+ end
634
+ end
635
+
636
+ should "work around buggy 1.8.* Tempfile equality" do
637
+ input = <<EOF
638
+ --AaB03x\r
639
+ content-disposition: form-data; name="huge"; filename="huge"\r
640
+ \r
641
+ foo\r
642
+ --AaB03x--
643
+ EOF
644
+
645
+ rack_input = Tempfile.new("rackspec")
646
+ rack_input.write(input)
647
+ rack_input.rewind
648
+
649
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
650
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
651
+ "CONTENT_LENGTH" => input.size,
652
+ :input => rack_input)
653
+
654
+ lambda{ req.POST }.should.not.raise
655
+ lambda{ req.POST }.should.not.raise("input re-processed!")
656
+ end
657
+
658
+ should "conform to the Rack spec" do
659
+ app = lambda { |env|
660
+ content = Rack::Request.new(env).POST["file"].inspect
661
+ size = content.respond_to?(:bytesize) ? content.bytesize : content.size
662
+ [200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]]
663
+ }
664
+
665
+ input = <<EOF
666
+ --AaB03x\r
667
+ content-disposition: form-data; name="reply"\r
668
+ \r
669
+ yes\r
670
+ --AaB03x\r
671
+ content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
672
+ Content-Type: image/jpeg\r
673
+ Content-Transfer-Encoding: base64\r
674
+ \r
675
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
676
+ --AaB03x--\r
677
+ EOF
678
+ input.force_encoding("ASCII-8BIT") if input.respond_to? :force_encoding
679
+ res = Rack::MockRequest.new(Rack::Lint.new(app)).get "/",
680
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
681
+ "CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input)
682
+
683
+ res.should.be.ok
684
+ end
685
+
686
+ should "parse Accept-Encoding correctly" do
687
+ parser = lambda do |x|
688
+ Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
689
+ end
690
+
691
+ parser.call(nil).should.equal([])
692
+
693
+ parser.call("compress, gzip").should.equal([["compress", 1.0], ["gzip", 1.0]])
694
+ parser.call("").should.equal([])
695
+ parser.call("*").should.equal([["*", 1.0]])
696
+ parser.call("compress;q=0.5, gzip;q=1.0").should.equal([["compress", 0.5], ["gzip", 1.0]])
697
+ parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").should.equal([["gzip", 1.0], ["identity", 0.5], ["*", 0] ])
698
+
699
+ lambda { parser.call("gzip ; q=1.0") }.should.raise(RuntimeError)
700
+ end
701
+
702
+ should 'provide ip information' do
703
+ app = lambda { |env|
704
+ request = Rack::Request.new(env)
705
+ response = Rack::Response.new
706
+ response.write request.ip
707
+ response.finish
708
+ }
709
+
710
+ mock = Rack::MockRequest.new(Rack::Lint.new(app))
711
+ res = mock.get '/', 'REMOTE_ADDR' => '123.123.123.123'
712
+ res.body.should.equal '123.123.123.123'
713
+
714
+ res = mock.get '/',
715
+ 'REMOTE_ADDR' => '123.123.123.123',
716
+ 'HTTP_X_FORWARDED_FOR' => '234.234.234.234'
717
+
718
+ res.body.should.equal '234.234.234.234'
719
+
720
+ res = mock.get '/',
721
+ 'REMOTE_ADDR' => '123.123.123.123',
722
+ 'HTTP_X_FORWARDED_FOR' => '234.234.234.234,212.212.212.212'
723
+
724
+ res.body.should.equal '234.234.234.234'
725
+
726
+ res = mock.get '/',
727
+ 'REMOTE_ADDR' => '123.123.123.123',
728
+ 'HTTP_X_FORWARDED_FOR' => 'unknown,234.234.234.234,212.212.212.212'
729
+
730
+ res.body.should.equal '234.234.234.234'
731
+ end
732
+
733
+ class MyRequest < Rack::Request
734
+ def params
735
+ {:foo => "bar"}
736
+ end
737
+ end
738
+
739
+ should "allow subclass request to be instantiated after parent request" do
740
+ env = Rack::MockRequest.env_for("/?foo=bar")
741
+
742
+ req1 = Rack::Request.new(env)
743
+ req1.GET.should.equal "foo" => "bar"
744
+ req1.params.should.equal "foo" => "bar"
745
+
746
+ req2 = MyRequest.new(env)
747
+ req2.GET.should.equal "foo" => "bar"
748
+ req2.params.should.equal :foo => "bar"
749
+ end
750
+
751
+ should "allow parent request to be instantiated after subclass request" do
752
+ env = Rack::MockRequest.env_for("/?foo=bar")
753
+
754
+ req1 = MyRequest.new(env)
755
+ req1.GET.should.equal "foo" => "bar"
756
+ req1.params.should.equal :foo => "bar"
757
+
758
+ req2 = Rack::Request.new(env)
759
+ req2.GET.should.equal "foo" => "bar"
760
+ req2.params.should.equal "foo" => "bar"
761
+ end
762
+
763
+ (0x20...0x7E).collect { |a|
764
+ b = a.chr
765
+ c = CGI.escape(b)
766
+ should "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do
767
+ url = "/?foo=#{c}bar#{c}"
768
+ env = Rack::MockRequest.env_for(url)
769
+ req2 = Rack::Request.new(env)
770
+ req2.GET.should.equal "foo" => "#{b}bar#{b}"
771
+ req2.params.should.equal "foo" => "#{b}bar#{b}"
772
+ end
773
+ }
774
+ end