rack-client 0.1.1 → 0.3.0

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 (76) hide show
  1. data/History.txt +2 -2
  2. data/README.textile +2 -2
  3. data/Rakefile +11 -5
  4. data/demo/demo_spec.rb +3 -3
  5. data/lib/rack/client.rb +29 -25
  6. data/lib/rack/client/adapter.rb +6 -0
  7. data/lib/rack/client/adapter/base.rb +57 -0
  8. data/lib/rack/client/adapter/simple.rb +49 -0
  9. data/lib/rack/client/auth/abstract/challenge.rb +53 -0
  10. data/lib/rack/client/auth/basic.rb +57 -0
  11. data/lib/rack/client/auth/digest/challenge.rb +38 -0
  12. data/lib/rack/client/auth/digest/md5.rb +78 -0
  13. data/lib/rack/client/auth/digest/params.rb +10 -0
  14. data/lib/rack/client/body.rb +12 -0
  15. data/lib/rack/client/cache.rb +19 -0
  16. data/lib/rack/client/cache/cachecontrol.rb +195 -0
  17. data/lib/rack/client/cache/context.rb +95 -0
  18. data/lib/rack/client/cache/entitystore.rb +77 -0
  19. data/lib/rack/client/cache/key.rb +51 -0
  20. data/lib/rack/client/cache/metastore.rb +133 -0
  21. data/lib/rack/client/cache/options.rb +147 -0
  22. data/lib/rack/client/cache/request.rb +46 -0
  23. data/lib/rack/client/cache/response.rb +62 -0
  24. data/lib/rack/client/cache/storage.rb +43 -0
  25. data/lib/rack/client/cookie_jar.rb +17 -0
  26. data/lib/rack/client/cookie_jar/context.rb +59 -0
  27. data/lib/rack/client/cookie_jar/cookie.rb +59 -0
  28. data/lib/rack/client/cookie_jar/cookiestore.rb +36 -0
  29. data/lib/rack/client/cookie_jar/options.rb +43 -0
  30. data/lib/rack/client/cookie_jar/request.rb +15 -0
  31. data/lib/rack/client/cookie_jar/response.rb +16 -0
  32. data/lib/rack/client/cookie_jar/storage.rb +34 -0
  33. data/lib/rack/client/dual_band.rb +13 -0
  34. data/lib/rack/client/follow_redirects.rb +47 -20
  35. data/lib/rack/client/handler.rb +10 -0
  36. data/lib/rack/client/handler/em-http.rb +66 -0
  37. data/lib/rack/client/handler/excon.rb +50 -0
  38. data/lib/rack/client/handler/net_http.rb +85 -0
  39. data/lib/rack/client/handler/typhoeus.rb +62 -0
  40. data/lib/rack/client/headers.rb +49 -0
  41. data/lib/rack/client/parser.rb +18 -0
  42. data/lib/rack/client/parser/base.rb +25 -0
  43. data/lib/rack/client/parser/body_collection.rb +50 -0
  44. data/lib/rack/client/parser/context.rb +15 -0
  45. data/lib/rack/client/parser/json.rb +54 -0
  46. data/lib/rack/client/parser/middleware.rb +8 -0
  47. data/lib/rack/client/parser/request.rb +21 -0
  48. data/lib/rack/client/parser/response.rb +19 -0
  49. data/lib/rack/client/parser/yaml.rb +52 -0
  50. data/lib/rack/client/response.rb +9 -0
  51. data/lib/rack/client/version.rb +5 -0
  52. data/spec/apps/example.org.ru +47 -3
  53. data/spec/auth/basic_spec.rb +69 -0
  54. data/spec/auth/digest/md5_spec.rb +69 -0
  55. data/spec/cache_spec.rb +40 -0
  56. data/spec/cookie_jar_spec.rb +37 -0
  57. data/spec/endpoint_spec.rb +4 -13
  58. data/spec/follow_redirect_spec.rb +27 -0
  59. data/spec/handler/async_api_spec.rb +69 -0
  60. data/spec/handler/em_http_spec.rb +22 -0
  61. data/spec/handler/excon_spec.rb +7 -0
  62. data/spec/handler/net_http_spec.rb +8 -0
  63. data/spec/handler/sync_api_spec.rb +55 -0
  64. data/spec/handler/typhoeus_spec.rb +22 -0
  65. data/spec/middleware_helper.rb +37 -0
  66. data/spec/middleware_spec.rb +48 -5
  67. data/spec/parser/json_spec.rb +22 -0
  68. data/spec/parser/yaml_spec.rb +22 -0
  69. data/spec/server_helper.rb +72 -0
  70. data/spec/spec_helper.rb +17 -3
  71. metadata +86 -31
  72. data/lib/rack/client/auth.rb +0 -13
  73. data/lib/rack/client/http.rb +0 -77
  74. data/spec/auth_spec.rb +0 -22
  75. data/spec/core_spec.rb +0 -123
  76. data/spec/redirect_spec.rb +0 -12
@@ -0,0 +1,9 @@
1
+ module Rack
2
+ module Client
3
+ class Response < Rack::Response
4
+ def initialize(status, headers, body)
5
+ super(body, status, headers)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ module Client
3
+ VERSION = "0.3.0"
4
+ end
5
+ end
@@ -23,8 +23,12 @@ class ExampleOrg < Sinatra::Base
23
23
  end
24
24
 
25
25
  put "/shelf/:book" do
26
- response["Location"] = "/shelf/#{params[:book]}"
27
- ""
26
+ if request.body.read == "some data"
27
+ response["Location"] = "/shelf/#{params[:book]}"
28
+ ""
29
+ else
30
+ raise "Not valid"
31
+ end
28
32
  end
29
33
 
30
34
  delete "/shelf/:book" do
@@ -45,6 +49,39 @@ class ExampleOrg < Sinatra::Base
45
49
  get "/no-etag" do
46
50
  ""
47
51
  end
52
+
53
+ get '/cacheable' do
54
+ if env['HTTP_IF_NONE_MATCH'] == '123456789abcde'
55
+ status 304
56
+ else
57
+ response['ETag'] = '123456789abcde'
58
+ Time.now.to_f.to_s
59
+ end
60
+ end
61
+
62
+ get '/cookied' do
63
+ if request.cookies.empty?
64
+ response.set_cookie('time', :domain => 'localhost', :path => '/', :value => Time.now.to_f)
65
+ response.set_cookie('time2', :domain => 'localhost', :path => '/cookied', :value => Time.now.to_f)
66
+ end
67
+ ''
68
+ end
69
+
70
+ get '/hash.:content_type' do |type|
71
+ case type
72
+ when 'json' then
73
+ content_type 'application/json'
74
+ {:foo => :bar}.to_json
75
+ when 'yaml', 'yml' then
76
+ content_type 'application/x-yaml'
77
+ {:foo => :bar}.to_yaml
78
+ end
79
+ end
80
+
81
+ post '/echo' do
82
+ content_type request.content_type
83
+ request.body.read
84
+ end
48
85
  end
49
86
 
50
87
  require 'pp'
@@ -69,13 +106,20 @@ use Rack::CommonLogger
69
106
  use Debug if ENV["DEBUG"]
70
107
 
71
108
  map "http://localhost/" do
72
- map "/auth/" do
109
+ map "/auth/basic/" do
73
110
  use Rack::Auth::Basic do |username,password|
74
111
  username == "username" && password == "password"
75
112
  end
76
113
  run ExampleOrg
77
114
  end
78
115
 
116
+ map "/auth/digest/" do
117
+ use DigestServer, 'My Realm' do |username|
118
+ {"username" => "password"}[username]
119
+ end
120
+ run ExampleOrg
121
+ end
122
+
79
123
  map "/" do
80
124
  run ExampleOrg
81
125
  end
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Rack::Client::Auth::Basic do
4
+ context 'Synchronous API' do
5
+ context 'with basic auth middleware' do
6
+ let(:client) do
7
+ Rack::Client.new("http://localhost:#{server.port}") do
8
+ use Rack::Client::Auth::Basic, "username", "password"
9
+ run Rack::Client::Handler::NetHTTP
10
+ end
11
+ end
12
+
13
+ it "succeeds with authorization" do
14
+ response = client.get("http://localhost:#{server.port}/auth/basic/ping")
15
+ response.status.should == 200
16
+ response.headers["Content-Type"].should == "text/html"
17
+ response.body.to_s.should == "pong"
18
+ end
19
+ end
20
+
21
+ context 'without basic auth middleware' do
22
+ let(:client) do
23
+ Rack::Client.new("http://localhost:#{server.port}") do
24
+ run Rack::Client::Handler::NetHTTP
25
+ end
26
+ end
27
+
28
+ it "fails with authorization" do
29
+ response = client.get("http://localhost:#{server.port}/auth/basic/ping")
30
+ response.status.should == 401
31
+ response.body.to_s.should == ""
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'Asynchronous API' do
37
+ context 'with basic auth middleware' do
38
+ let(:client) do
39
+ Rack::Client.new("http://localhost:#{server.port}") do
40
+ use Rack::Client::Auth::Basic, "username", "password"
41
+ run Rack::Client::Handler::NetHTTP
42
+ end
43
+ end
44
+
45
+ it "succeeds with authorization" do
46
+ client.get("http://localhost:#{server.port}/auth/basic/ping") do |response|
47
+ response.status.should == 200
48
+ response.headers["Content-Type"].should == "text/html"
49
+ response.body.to_s.should == "pong"
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'without basic auth middleware' do
55
+ let(:client) do
56
+ Rack::Client.new("http://localhost:#{server.port}") do
57
+ run Rack::Client::Handler::NetHTTP
58
+ end
59
+ end
60
+
61
+ it "fails with authorization" do
62
+ client.get("http://localhost:#{server.port}/auth/basic/ping") do |response|
63
+ response.status.should == 401
64
+ response.body.to_s.should == ""
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Rack::Client::Auth::Basic do
4
+ context 'Synchronous API' do
5
+ context 'with digest auth middleware' do
6
+ let(:client) do
7
+ Rack::Client.new("http://localhost:#{server.port}") do
8
+ use Rack::Client::Auth::Digest::MD5, "My Realm", "username", "password"
9
+ run Rack::Client::Handler::NetHTTP
10
+ end
11
+ end
12
+
13
+ it "succeeds with authorization" do
14
+ response = client.get("http://localhost:#{server.port}/auth/digest/ping")
15
+ response.status.should == 200
16
+ response.headers["Content-Type"].should == "text/html"
17
+ response.body.to_s.should == "pong"
18
+ end
19
+ end
20
+
21
+ context 'without digest auth middleware' do
22
+ let(:client) do
23
+ Rack::Client.new("http://localhost:#{server.port}") do
24
+ run Rack::Client::Handler::NetHTTP
25
+ end
26
+ end
27
+
28
+ it "fails with authorization" do
29
+ response = client.get("http://localhost:#{server.port}/auth/digest/ping")
30
+ response.status.should == 401
31
+ response.body.to_s.should == ""
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'Asynchronous API' do
37
+ context 'with digest auth middleware' do
38
+ let(:client) do
39
+ Rack::Client.new("http://localhost:#{server.port}") do
40
+ use Rack::Client::Auth::Digest::MD5, "My Realm", "username", "password"
41
+ run Rack::Client::Handler::NetHTTP
42
+ end
43
+ end
44
+
45
+ it "succeeds with authorization" do
46
+ response = client.get("http://localhost:#{server.port}/auth/digest/ping") do |response|
47
+ response.status.should == 200
48
+ response.headers["Content-Type"].should == "text/html"
49
+ response.body.to_s.should == "pong"
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'without digest auth middleware' do
55
+ let(:client) do
56
+ Rack::Client.new("http://localhost:#{server.port}") do
57
+ run Rack::Client::Handler::NetHTTP
58
+ end
59
+ end
60
+
61
+ it "fails with authorization" do
62
+ response = client.get("http://localhost:#{server.port}/auth/digest/ping") do |response|
63
+ response.status.should == 401
64
+ response.body.to_s.should == ""
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Rack::Client::Cache do
4
+ let(:client) do
5
+ Rack::Client.new("http://localhost:#{server.port}") do
6
+ use Rack::Client::Cache
7
+ run Rack::Client::Handler::NetHTTP
8
+ end
9
+ end
10
+
11
+ after(:each) do
12
+ Rack::Client::Cache::Storage.instance.clear
13
+ end
14
+
15
+ context 'Synchronous API' do
16
+ it "successfully retrieves cache hits" do
17
+ response = client.get("http://localhost:#{server.port}/cacheable")
18
+ response.headers['X-Rack-Client-Cache'].should include('store')
19
+ original_body = response.body
20
+
21
+ response = client.get("http://localhost:#{server.port}/cacheable")
22
+ response.headers['X-Rack-Client-Cache'].should include('fresh')
23
+ response.body.should == original_body
24
+ end
25
+ end
26
+
27
+ context 'Asynchronous API' do
28
+ it "successfully retrieves cache hits" do
29
+ client.get("http://localhost:#{server.port}/cacheable") do |response|
30
+ response.headers['X-Rack-Client-Cache'].should include('store')
31
+ original_body = response.body
32
+
33
+ client.get("http://localhost:#{server.port}/cacheable") do |response|
34
+ response.headers['X-Rack-Client-Cache'].should include('fresh')
35
+ response.body.should == original_body
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Rack::Client::CookieJar do
4
+ let(:client) do
5
+ Rack::Client.new("http://localhost:#{server.port}") do
6
+ use Rack::Client::CookieJar
7
+ run Rack::Client::Handler::NetHTTP
8
+ end
9
+ end
10
+
11
+ context 'Synchronous API' do
12
+ it 'serves the cookie in future responses' do
13
+ response = client.get("http://localhost:#{server.port}/cookied")
14
+ response['Set-Cookie'].should_not be_nil
15
+ response['rack-client-cookiejar.cookies'].should_not be_nil
16
+
17
+ response = client.get("http://localhost:#{server.port}/cookied")
18
+ response['Set-Cookie'].should be_nil
19
+ response['rack-client-cookiejar.cookies'].should_not be_nil
20
+ end
21
+ end
22
+
23
+ context 'Asynchronous API' do
24
+ it 'serves the cookie in future responses' do
25
+ client.get("http://localhost:#{server.port}/cookied") do |response|
26
+ response['Set-Cookie'].should_not be_nil
27
+ response['rack-client-cookiejar.cookies'].should_not be_nil
28
+
29
+ client.get("http://localhost:#{server.port}/cookied") do |response|
30
+ response['Set-Cookie'].should be_nil
31
+ response['rack-client-cookiejar.cookies'].should_not be_nil
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -9,29 +9,20 @@ class MyApp < Sinatra::Base
9
9
  end
10
10
 
11
11
  describe Rack::Client, "with an Rack app endpoint" do
12
- it "returns the body" do
13
- client = Rack::Client.new do
12
+ let(:client) do
13
+ Rack::Client.new("http://localhost:#{server.port}") do
14
14
  run Rack::URLMap.new("http://example.org/" => MyApp)
15
15
  end
16
- response = client.get("http://example.org/awesome")
17
- response.status.should == 200
18
- response.body.should == "test"
19
16
  end
20
17
 
21
18
  describe "with a custom domain" do
22
19
  it "returns the body" do
23
- client = Rack::Client.new do
24
- run Rack::URLMap.new("http://google.com/" => MyApp)
25
- end
26
- response = client.get("http://google.com/awesome")
20
+ response = client.get("http://example.org/awesome")
27
21
  response.status.should == 200
28
- response.body.should == "test"
22
+ response.body.to_s.should == "test"
29
23
  end
30
24
 
31
25
  it "only functions for that domain" do
32
- client = Rack::Client.new do
33
- run Rack::URLMap.new("http://google.com/" => MyApp)
34
- end
35
26
  response = client.get("http://example.org/")
36
27
  response.status.should == 404
37
28
  end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Rack::Client::FollowRedirects do
4
+ let(:client) do
5
+ Rack::Client.new("http://localhost:#{server.port}") do
6
+ use Rack::Client::FollowRedirects
7
+ run Rack::Client::Handler::NetHTTP
8
+ end
9
+ end
10
+
11
+ context 'Synchronous API' do
12
+ it "follows redirects" do
13
+ response = client.get("http://localhost:#{server.port}/redirect")
14
+ response.status.should == 200
15
+ response.body.to_s.should == "after redirect"
16
+ end
17
+ end
18
+
19
+ context 'Asynchronous API' do
20
+ it "follows redirects" do
21
+ client.get("http://localhost:#{server.port}/redirect") do |response|
22
+ response.status.should == 200
23
+ response.body.to_s.should == "after redirect"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ share_examples_for "Asynchronous Request API" do
4
+ context 'DELETE request' do
5
+ it 'can handle a No Content response' do
6
+ client.delete("/shelf/ctm") do |response|
7
+ response.status.should == 204
8
+ finish
9
+ end
10
+ end
11
+ end
12
+
13
+ context 'GET request' do
14
+ it 'has the proper response for a basic request' do
15
+ client.get("/ping") do |response|
16
+ response.status.should == 200
17
+ response.body.to_s.should == 'pong'
18
+ response["Content-Type"].should == 'text/html'
19
+ response["Content-Length"].to_i.should == 4
20
+ finish
21
+ end
22
+ end
23
+
24
+ it 'can handle a temporary redirect response' do
25
+ client.get("/redirect") do |response|
26
+ response.status.should == 302
27
+ response["Location"].should == "/after-redirect"
28
+ finish
29
+ end
30
+ end
31
+
32
+ it 'can return an empty body' do
33
+ client.get("/empty") do |response|
34
+ response.body.should be_empty
35
+ finish
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'HEAD request' do
41
+ it 'can handle ETag headers' do
42
+ client.head("/shelf") do |response|
43
+ response.status.should == 200
44
+ response["ETag"].should == "828ef3fdfa96f00ad9f27c383fc9ac7f"
45
+ finish
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'POST request' do
51
+ it 'can send a request body' do
52
+ client.post("/posted", {}, "some data") do |response|
53
+ response.status.should == 201
54
+ response["Created"].should == "awesome"
55
+ finish
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'PUT request' do
61
+ it 'can send a request body' do
62
+ client.put("/shelf/ctm", {}, "some data") do |response|
63
+ response.status.should == 200
64
+ response["Location"].should == "/shelf/ctm"
65
+ finish
66
+ end
67
+ end
68
+ end
69
+ end