rack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (54) hide show
  1. data/AUTHORS +3 -0
  2. data/COPYING +18 -0
  3. data/KNOWN-ISSUES +18 -0
  4. data/RDOX +144 -0
  5. data/README +154 -0
  6. data/Rakefile +174 -0
  7. data/SPEC +132 -0
  8. data/bin/rackup +148 -0
  9. data/contrib/rack_logo.svg +111 -0
  10. data/example/lobster.ru +4 -0
  11. data/lib/rack.rb +67 -0
  12. data/lib/rack/adapter/camping.rb +16 -0
  13. data/lib/rack/adapter/rails.rb +65 -0
  14. data/lib/rack/builder.rb +52 -0
  15. data/lib/rack/cascade.rb +26 -0
  16. data/lib/rack/commonlogger.rb +56 -0
  17. data/lib/rack/file.rb +108 -0
  18. data/lib/rack/handler/cgi.rb +57 -0
  19. data/lib/rack/handler/fastcgi.rb +81 -0
  20. data/lib/rack/handler/mongrel.rb +57 -0
  21. data/lib/rack/handler/webrick.rb +56 -0
  22. data/lib/rack/lint.rb +394 -0
  23. data/lib/rack/lobster.rb +65 -0
  24. data/lib/rack/mock.rb +183 -0
  25. data/lib/rack/recursive.rb +57 -0
  26. data/lib/rack/reloader.rb +64 -0
  27. data/lib/rack/request.rb +112 -0
  28. data/lib/rack/response.rb +114 -0
  29. data/lib/rack/showexceptions.rb +344 -0
  30. data/lib/rack/urlmap.rb +50 -0
  31. data/lib/rack/utils.rb +176 -0
  32. data/test/cgi/lighttpd.conf +20 -0
  33. data/test/cgi/test +9 -0
  34. data/test/cgi/test.fcgi +9 -0
  35. data/test/cgi/test.ru +7 -0
  36. data/test/spec_rack_camping.rb +44 -0
  37. data/test/spec_rack_cascade.rb +35 -0
  38. data/test/spec_rack_cgi.rb +82 -0
  39. data/test/spec_rack_commonlogger.rb +32 -0
  40. data/test/spec_rack_fastcgi.rb +82 -0
  41. data/test/spec_rack_file.rb +32 -0
  42. data/test/spec_rack_lint.rb +317 -0
  43. data/test/spec_rack_lobster.rb +45 -0
  44. data/test/spec_rack_mock.rb +150 -0
  45. data/test/spec_rack_mongrel.rb +87 -0
  46. data/test/spec_rack_recursive.rb +77 -0
  47. data/test/spec_rack_request.rb +219 -0
  48. data/test/spec_rack_response.rb +110 -0
  49. data/test/spec_rack_showexceptions.rb +21 -0
  50. data/test/spec_rack_urlmap.rb +140 -0
  51. data/test/spec_rack_utils.rb +57 -0
  52. data/test/spec_rack_webrick.rb +89 -0
  53. data/test/testrequest.rb +43 -0
  54. metadata +117 -0
@@ -0,0 +1,110 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/response'
4
+
5
+ context "Rack::Response" do
6
+ specify "has sensible default values" do
7
+ response = Rack::Response.new
8
+ status, header, body = response.finish
9
+ status.should.equal 200
10
+ header.should.equal "Content-Type" => "text/html"
11
+ body.each { |part|
12
+ part.should.equal ""
13
+ }
14
+
15
+ response = Rack::Response.new
16
+ status, header, body = *response
17
+ status.should.equal 200
18
+ header.should.equal "Content-Type" => "text/html"
19
+ body.each { |part|
20
+ part.should.equal ""
21
+ }
22
+ end
23
+
24
+ specify "can be written to" do
25
+ response = Rack::Response.new
26
+
27
+ status, header, body = response.finish do
28
+ response.write "foo"
29
+ response.write "bar"
30
+ response.write "baz"
31
+ end
32
+
33
+ parts = []
34
+ body.each { |part| parts << part }
35
+
36
+ parts.should.equal ["foo", "bar", "baz"]
37
+ end
38
+
39
+ specify "can set and read headers" do
40
+ response = Rack::Response.new
41
+ response["Content-Type"].should.equal "text/html"
42
+ response["Content-Type"] = "text/plain"
43
+ response["Content-Type"].should.equal "text/plain"
44
+ end
45
+
46
+ specify "can set cookies" do
47
+ response = Rack::Response.new
48
+
49
+ response.set_cookie "foo", "bar"
50
+ response["Set-Cookie"].should.equal "foo=bar"
51
+ response.set_cookie "foo2", "bar2"
52
+ response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2"]
53
+ response.set_cookie "foo3", "bar3"
54
+ response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2", "foo3=bar3"]
55
+ end
56
+
57
+ specify "can delete cookies" do
58
+ response = Rack::Response.new
59
+ response.set_cookie "foo", "bar"
60
+ response.set_cookie "foo2", "bar2"
61
+ response.delete_cookie "foo"
62
+ response["Set-Cookie"].should.equal ["foo2=bar2",
63
+ "foo=; expires=Thu, 01 Jan 1970 00:00:00 GMT"]
64
+ end
65
+
66
+ specify "has a useful constructor" do
67
+ r = Rack::Response.new("foo")
68
+ status, header, body = r.finish
69
+ str = ""; body.each { |part| str << part }
70
+ str.should.equal "foo"
71
+
72
+ r = Rack::Response.new(["foo", "bar"])
73
+ status, header, body = r.finish
74
+ str = ""; body.each { |part| str << part }
75
+ str.should.equal "foobar"
76
+
77
+ r = Rack::Response.new({"foo", "bar"})
78
+ r.write "foo"
79
+ status, header, body = r.finish
80
+ str = ""; body.each { |part| str << part }
81
+ str.should.equal "foobarfoo"
82
+
83
+ r = Rack::Response.new([], 500)
84
+ r.status.should.equal 500
85
+ end
86
+
87
+ specify "has a constructor that can take a block" do
88
+ r = Rack::Response.new { |res|
89
+ res.status = 404
90
+ res.write "foo"
91
+ }
92
+ status, header, body = r.finish
93
+ str = ""; body.each { |part| str << part }
94
+ str.should.equal "foo"
95
+ status.should.equal 404
96
+ end
97
+
98
+ specify "doesn't return invalid responses" do
99
+ r = Rack::Response.new(["foo", "bar"], 201)
100
+ status, header, body = r.finish
101
+ str = ""; body.each { |part| str << part }
102
+ str.should.be.empty
103
+ header["Content-Type"].should.equal nil
104
+
105
+ lambda {
106
+ Rack::Response.new(Object.new)
107
+ }.should.raise(TypeError).
108
+ message.should =~ /String or iterable required/
109
+ end
110
+ end
@@ -0,0 +1,21 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/showexceptions'
4
+ require 'rack/mock'
5
+
6
+ context "Rack::ShowExceptions" do
7
+ specify "catches exceptions" do
8
+ res = nil
9
+ req = Rack::MockRequest.new(Rack::ShowExceptions.new(lambda { |env|
10
+ raise RuntimeError
11
+ }))
12
+ lambda {
13
+ res = req.get("/")
14
+ }.should.not.raise
15
+ res.should.be.a.server_error
16
+ res.status.should.equal 500
17
+
18
+ res.should =~ /RuntimeError/
19
+ res.should =~ /ShowExceptions/
20
+ end
21
+ end
@@ -0,0 +1,140 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/urlmap'
4
+ require 'rack/mock'
5
+
6
+ context "Rack::URLMap" do
7
+ specify "dispatches paths correctly" do
8
+ map = Rack::URLMap.new("/foo" => lambda { |env|
9
+ [200,
10
+ { "Content-Type" => "text/plain",
11
+ "X-Position" => "/foo",
12
+ "X-PathInfo" => env["PATH_INFO"],
13
+ }, [""]]},
14
+
15
+ "/bar" => lambda { |env|
16
+ [200,
17
+ { "Content-Type" => "text/plain",
18
+ "X-Position" => "/bar",
19
+ "X-PathInfo" => env["PATH_INFO"],
20
+ }, [""]]},
21
+
22
+ "/foo/bar" => lambda { |env|
23
+ [200,
24
+ { "Content-Type" => "text/plain",
25
+ "X-Position" => "/foo/bar",
26
+ "X-PathInfo" => env["PATH_INFO"],
27
+ }, [""]]}
28
+ )
29
+
30
+
31
+ Rack::MockRequest.new(map).get("/").should.be.not_found
32
+
33
+ res = Rack::MockRequest.new(map).get("/foo")
34
+ res.should.be.ok
35
+ res["X-Position"].should.equal "/foo"
36
+ res["X-PathInfo"].should.equal "/"
37
+
38
+
39
+ res = Rack::MockRequest.new(map).get("/bar")
40
+ res.should.be.ok
41
+ res["X-Position"].should.equal "/bar"
42
+ res["X-PathInfo"].should.equal "/"
43
+
44
+ res = Rack::MockRequest.new(map).get("/foo/quux")
45
+ res.should.be.ok
46
+ res["X-Position"].should.equal "/foo"
47
+ res["X-PathInfo"].should.equal "/quux"
48
+
49
+ res = Rack::MockRequest.new(map).get("/foo/quux", "SCRIPT_NAME" => "/bleh")
50
+ res.should.be.ok
51
+ res["X-Position"].should.equal "/foo"
52
+ res["X-PathInfo"].should.equal "/quux"
53
+ end
54
+
55
+ specify "dispatches hosts correctly" do
56
+ map = Rack::URLMap.new("http://foo.org/" => lambda { |env|
57
+ [200,
58
+ { "Content-Type" => "text/plain",
59
+ "X-Position" => "foo.org",
60
+ "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
61
+ }, [""]]},
62
+ "http://bar.org/" => lambda { |env|
63
+ [200,
64
+ { "Content-Type" => "text/plain",
65
+ "X-Position" => "bar.org",
66
+ "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
67
+ }, [""]]},
68
+ "/" => lambda { |env|
69
+ [200,
70
+ { "Content-Type" => "text/plain",
71
+ "X-Position" => "default.org",
72
+ "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
73
+ }, [""]]}
74
+ )
75
+
76
+ res = Rack::MockRequest.new(map).get("/")
77
+ res.should.be.ok
78
+ res["X-Position"].should.equal "default.org"
79
+
80
+ res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "bar.org")
81
+ res.should.be.ok
82
+ res["X-Position"].should.equal "bar.org"
83
+
84
+ res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "foo.org")
85
+ res.should.be.ok
86
+ res["X-Position"].should.equal "foo.org"
87
+
88
+ res = Rack::MockRequest.new(map).get("http://foo.org/")
89
+ res.should.be.ok
90
+ res["X-Position"].should.equal "default.org"
91
+
92
+ res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "example.org")
93
+ res.should.be.ok
94
+ res["X-Position"].should.equal "default.org"
95
+
96
+ res = Rack::MockRequest.new(map).get("/",
97
+ "HTTP_HOST" => "example.org:9292",
98
+ "SERVER_PORT" => "9292")
99
+ res.should.be.ok
100
+ res["X-Position"].should.equal "default.org"
101
+ end
102
+
103
+ specify "should be nestable" do
104
+ map = Rack::URLMap.new("/foo" =>
105
+ Rack::URLMap.new("/bar" => lambda { |env|
106
+ [200,
107
+ { "Content-Type" => "text/plain",
108
+ "X-Position" => "/foo/bar",
109
+ "X-PathInfo" => env["PATH_INFO"],
110
+ }, [""]]}
111
+ )
112
+ )
113
+
114
+ res = Rack::MockRequest.new(map).get("/foo/bar")
115
+ res.should.be.ok
116
+ res["X-Position"].should.equal "/foo/bar"
117
+ end
118
+
119
+ specify "should route root apps correctly" do
120
+ map = Rack::URLMap.new("/" => lambda { |env|
121
+ [200,
122
+ { "Content-Type" => "text/plain",
123
+ "X-Position" => "root",
124
+ }, [""]]},
125
+ "/foo" => lambda { |env|
126
+ [200,
127
+ { "Content-Type" => "text/plain",
128
+ "X-Position" => "foo",
129
+ }, [""]]}
130
+ )
131
+
132
+ res = Rack::MockRequest.new(map).get("/foo/bar")
133
+ res.should.be.ok
134
+ res["X-Position"].should.equal "foo"
135
+
136
+ res = Rack::MockRequest.new(map).get("/bar")
137
+ res.should.be.ok
138
+ res["X-Position"].should.equal "root"
139
+ end
140
+ end
@@ -0,0 +1,57 @@
1
+ require 'rack/utils'
2
+
3
+ context "Rack::Utils" do
4
+ specify "should escape correctly" do
5
+ Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
6
+ Rack::Utils.escape("a space").should.equal "a+space"
7
+ Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\").
8
+ should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C"
9
+ end
10
+
11
+ specify "should unescape correctly" do
12
+ Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fo<o>bar"
13
+ Rack::Utils.unescape("a+space").should.equal "a space"
14
+ Rack::Utils.unescape("a%20space").should.equal "a space"
15
+ Rack::Utils.unescape("q1%212%22%27w%245%267%2Fz8%29%3F%5C").
16
+ should.equal "q1!2\"'w$5&7/z8)?\\"
17
+ end
18
+
19
+ specify "should parse queries correctly" do
20
+ Rack::Utils.parse_query("foo=bar").should.equal "foo" => "bar"
21
+ Rack::Utils.parse_query("foo=bar&foo=quux").
22
+ should.equal "foo" => ["bar", "quux"]
23
+ Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
24
+ should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
25
+ end
26
+ end
27
+
28
+ context "Rack::Utils::HeaderHash" do
29
+ specify "should capitalize on all accesses" do
30
+ h = Rack::Utils::HeaderHash.new("foo" => "bar")
31
+ h["foo"].should.equal "bar"
32
+ h["Foo"].should.equal "bar"
33
+ h["FOO"].should.equal "bar"
34
+
35
+ h.to_hash.should.equal "Foo" => "bar"
36
+
37
+ h["bar-zzle"] = "quux"
38
+
39
+ h.to_hash.should.equal "Foo" => "bar", "Bar-Zzle" => "quux"
40
+ end
41
+
42
+ specify "should capitalize correctly" do
43
+ h = Rack::Utils::HeaderHash.new
44
+
45
+ h.capitalize("foo").should.equal "Foo"
46
+ h.capitalize("foo-bar").should.equal "Foo-Bar"
47
+ h.capitalize("foo_bar").should.equal "Foo_Bar"
48
+ h.capitalize("foo bar").should.equal "Foo Bar"
49
+ h.capitalize("foo-bar-quux").should.equal "Foo-Bar-Quux"
50
+ h.capitalize("foo-bar-2quux").should.equal "Foo-Bar-2quux"
51
+ end
52
+
53
+ specify "should be converted to real Hash" do
54
+ h = Rack::Utils::HeaderHash.new("foo" => "bar")
55
+ h.to_hash.should.be.instance_of Hash
56
+ end
57
+ end
@@ -0,0 +1,89 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/handler/webrick'
4
+ require 'rack/lint'
5
+ require 'testrequest'
6
+
7
+ Thread.abort_on_exception = true
8
+
9
+ context "Rack::Handler::WEBrick" do
10
+ include TestRequest::Helpers
11
+
12
+ setup do
13
+ @server = WEBrick::HTTPServer.new(:Host => @host='0.0.0.0',
14
+ :Port => @port=9202,
15
+ :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
16
+ :AccessLog => [])
17
+ @server.mount "/test", Rack::Handler::WEBrick,
18
+ Rack::Lint.new(TestRequest.new)
19
+ Thread.new { @server.start }
20
+ trap(:INT) { @server.shutdown }
21
+ end
22
+
23
+ specify "should respond" do
24
+ lambda {
25
+ GET("/test")
26
+ }.should.not.raise
27
+ end
28
+
29
+ specify "should be a WEBrick" do
30
+ GET("/test")
31
+ status.should.be 200
32
+ response["SERVER_SOFTWARE"].should =~ /WEBrick/
33
+ response["HTTP_VERSION"].should.equal "HTTP/1.1"
34
+ response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
35
+ response["SERVER_PORT"].should.equal "9202"
36
+ response["SERVER_NAME"].should.equal "0.0.0.0"
37
+ end
38
+
39
+ specify "should have rack headers" do
40
+ GET("/test")
41
+ response["rack.version"].should.equal [0,1]
42
+ response["rack.multithread"].should.be true
43
+ response["rack.multiprocess"].should.be false
44
+ response["rack.run_once"].should.be false
45
+ end
46
+
47
+ specify "should have CGI headers on GET" do
48
+ GET("/test")
49
+ response["REQUEST_METHOD"].should.equal "GET"
50
+ response["SCRIPT_NAME"].should.equal "/test"
51
+ response["REQUEST_PATH"].should.equal "/"
52
+ response["PATH_INFO"].should.be.nil
53
+ response["QUERY_STRING"].should.equal ""
54
+ response["test.postdata"].should.equal ""
55
+
56
+ GET("/test/foo?quux=1")
57
+ response["REQUEST_METHOD"].should.equal "GET"
58
+ response["SCRIPT_NAME"].should.equal "/test"
59
+ response["REQUEST_PATH"].should.equal "/"
60
+ response["PATH_INFO"].should.equal "/foo"
61
+ response["QUERY_STRING"].should.equal "quux=1"
62
+ end
63
+
64
+ specify "should have CGI headers on POST" do
65
+ POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
66
+ status.should.equal 200
67
+ response["REQUEST_METHOD"].should.equal "POST"
68
+ response["SCRIPT_NAME"].should.equal "/test"
69
+ response["REQUEST_PATH"].should.equal "/"
70
+ response["QUERY_STRING"].should.equal ""
71
+ response["HTTP_X_TEST_HEADER"].should.equal "42"
72
+ response["test.postdata"].should.equal "rack-form-data=23"
73
+ end
74
+
75
+ specify "should support HTTP auth" do
76
+ GET("/test", {:user => "ruth", :passwd => "secret"})
77
+ response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
78
+ end
79
+
80
+ specify "should set status" do
81
+ GET("/test?secret")
82
+ status.should.equal 403
83
+ response["rack.url_scheme"].should.equal "http"
84
+ end
85
+
86
+ teardown do
87
+ @server.shutdown
88
+ end
89
+ end
@@ -0,0 +1,43 @@
1
+ require 'yaml'
2
+ require 'net/http'
3
+
4
+ class TestRequest
5
+ def call(env)
6
+ status = env["QUERY_STRING"] =~ /secret/ ? 403 : 200
7
+ env["test.postdata"] = env["rack.input"].read
8
+ [status, {"Content-Type" => "text/yaml"}, [env.to_yaml]]
9
+ end
10
+
11
+ module Helpers
12
+ attr_reader :status, :response
13
+
14
+ def GET(path, header={})
15
+ Net::HTTP.start(@host, @port) { |http|
16
+ user = header.delete(:user)
17
+ passwd = header.delete(:passwd)
18
+
19
+ get = Net::HTTP::Get.new(path, header)
20
+ get.basic_auth user, passwd if user && passwd
21
+ http.request(get) { |response|
22
+ @status = response.code.to_i
23
+ @response = YAML.load(response.body)
24
+ }
25
+ }
26
+ end
27
+
28
+ def POST(path, formdata={}, header={})
29
+ Net::HTTP.start(@host, @port) { |http|
30
+ user = header.delete(:user)
31
+ passwd = header.delete(:passwd)
32
+
33
+ post = Net::HTTP::Post.new(path, header)
34
+ post.form_data = formdata
35
+ post.basic_auth user, passwd if user && passwd
36
+ http.request(post) { |response|
37
+ @status = response.code.to_i
38
+ @response = YAML.load(response.body)
39
+ }
40
+ }
41
+ end
42
+ end
43
+ end