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,85 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+
4
+ module Rack
5
+ module Client
6
+ module Handler
7
+ class NetHTTP
8
+ include Rack::Client::DualBand
9
+
10
+ class << self
11
+ extend Forwardable
12
+ def_delegator :new, :call
13
+ end
14
+
15
+ def sync_call(env)
16
+ request = Rack::Request.new(env)
17
+
18
+ connection_for(request).request(net_request_for(request), body_for(request)) do |net_response|
19
+ return parse(net_response).finish
20
+ end
21
+ end
22
+
23
+ def async_call(env)
24
+ request = Rack::Request.new(env)
25
+
26
+ connection_for(request).request(net_request_for(request), body_for(request)) do |net_response|
27
+ yield parse(net_response).finish
28
+ end
29
+ end
30
+
31
+ def connection_for(request)
32
+ connection = Net::HTTP.new(request.host, request.port)
33
+
34
+ if request.scheme == 'https'
35
+ connection.use_ssl = true
36
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
37
+ end
38
+
39
+ connection.start
40
+ connection
41
+ end
42
+
43
+ def net_request_for(request)
44
+ klass = case request.request_method
45
+ when 'DELETE' then Net::HTTP::Delete
46
+ when 'GET' then Net::HTTP::Get
47
+ when 'HEAD' then Net::HTTP::Head
48
+ when 'POST' then Net::HTTP::Post
49
+ when 'PUT' then Net::HTTP::Put
50
+ end
51
+
52
+ klass.new(request.path, Headers.from(request.env).to_http)
53
+ end
54
+
55
+ def body_for(request)
56
+ case request.body
57
+ when StringIO then request.body.string
58
+ when IO then request.body.read
59
+ when Array then request.body.to_s
60
+ when String then request.body
61
+ end
62
+ end
63
+
64
+ def parse(net_response)
65
+ body = (net_response.body.nil? || net_response.body.empty?) ? [] : StringIO.new(net_response.body)
66
+ Response.new(net_response.code.to_i, parse_headers(net_response), body)
67
+ end
68
+
69
+ def parse_headers(net_response)
70
+ headers = Headers.new
71
+
72
+ net_response.each do |(k,v)|
73
+ headers.update(k => v)
74
+ end
75
+
76
+ headers.to_http
77
+ end
78
+
79
+ def connections
80
+ @connections ||= {}
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,62 @@
1
+ require 'typhoeus'
2
+
3
+ module Rack
4
+ module Client
5
+ module Handler
6
+ class Typhoeus
7
+ include Rack::Client::DualBand
8
+
9
+ def initialize(url, hydra = Typhoeus::Hydra.new)
10
+ @uri, @hydra = URI.parse(url), hydra
11
+ end
12
+
13
+ def async_call(env)
14
+ rack_request = Rack::Request.new(env)
15
+
16
+ typhoeus_request = request_for(rack_request)
17
+
18
+ typhoeus_request.on_complete do |response|
19
+ yield parse(response).finish
20
+ end
21
+
22
+ @hydra.queue typhoeus_request
23
+ end
24
+
25
+ def sync_call(env)
26
+ rack_request = Rack::Request.new(env)
27
+
28
+ parse(process(rack_request)).finish
29
+ end
30
+
31
+ def parse(typhoeus_response)
32
+ body = (typhoeus_response.body.nil? || typhoeus_response.body.empty?) ? [] : StringIO.new(typhoeus_response.body)
33
+ Response.new(typhoeus_response.code, Headers.new(typhoeus_response.headers_hash).to_http, body)
34
+ end
35
+
36
+ def request_for(rack_request)
37
+ ::Typhoeus::Request.new((@uri + rack_request.path).to_s, params_for(rack_request))
38
+ end
39
+
40
+ def process(rack_request)
41
+ ::Typhoeus::Request.run((@uri + rack_request.path).to_s, params_for(rack_request))
42
+ end
43
+
44
+ def params_for(rack_request)
45
+ {
46
+ :method => rack_request.request_method.downcase.to_sym,
47
+ :headers => Headers.from(rack_request.env).to_http,
48
+ :params => {}
49
+ }.merge(body_params_for(rack_request))
50
+ end
51
+
52
+ def body_params_for(rack_request)
53
+ unless %w[ HEAD GET ].include? rack_request.request_method
54
+ {:body => rack_request.body.string}
55
+ else
56
+ {}
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,49 @@
1
+ module Rack
2
+ module Client
3
+ class Headers < Hash
4
+ def self.from(env)
5
+ new env.reject {|(header,_)| header !~ %r'^HTTP_' unless %w( CONTENT_TYPE CONTENT_LENGTH ).include?(header) }
6
+ end
7
+
8
+ def initialize(headers = {})
9
+ super
10
+ merge!(headers)
11
+ end
12
+
13
+ def clean(header)
14
+ header.gsub(/HTTP_/, '').gsub('_', '-').gsub(/(\w+)/) do |matches|
15
+ matches.downcase.sub(/^./) do |char|
16
+ char.upcase
17
+ end
18
+ end
19
+ end
20
+
21
+ def to_http
22
+ self.inject({}) {|h,(header,value)| h.update(clean(header) => value) }
23
+ end
24
+
25
+ def to_env
26
+ self.inject({}) {|h,(header,value)| h.update((rackish?(header) ? header : rackify(header)) => value) }
27
+ end
28
+
29
+ def rackish?(header)
30
+ case header
31
+ when 'CONTENT_TYPE', 'CONTENT_LENGTH' then true
32
+ when /^rack[-.]/ then true
33
+ when /^HTTP_/ then true
34
+ else false
35
+ end
36
+ end
37
+
38
+ def rackify(original)
39
+ header = original.upcase.gsub('-', '_')
40
+
41
+ case header
42
+ when 'CONTENT_TYPE', 'CONTENT_LENGTH' then header
43
+ when /^HTTP_/ then header
44
+ else "HTTP_#{header}"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ module Rack
2
+ module Client
3
+ module Parser
4
+ autoload :JSON, 'rack/client/parser/json'
5
+ autoload :YAML, 'rack/client/parser/yaml'
6
+
7
+ def self.new(app, &b)
8
+ Context.new(app, &b)
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ require 'rack/client/parser/base'
15
+ require 'rack/client/parser/body_collection'
16
+ require 'rack/client/parser/context'
17
+ require 'rack/client/parser/request'
18
+ require 'rack/client/parser/response'
@@ -0,0 +1,25 @@
1
+ module Rack
2
+ module Client
3
+ module Parser
4
+ class Base
5
+ CONTENT_TYPE = %r'^([^/]+)/([^;]+)\s?(?:;\s?(.*))?$'
6
+
7
+ @@type_table ||= Hash.new {|h,k| h[k] = Hash.new {|hh,kk| hh[kk] = {} } }
8
+
9
+ def self.content_type(type, subtype, *parameters)
10
+ type_table[type][subtype][parameters] = self
11
+ end
12
+
13
+ def self.type_table
14
+ @@type_table
15
+ end
16
+
17
+ def self.lookup(content_type)
18
+ type, subtype, *parameters = content_type.scan(CONTENT_TYPE).first
19
+
20
+ type_table[type][subtype][parameters.compact]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ module Rack
2
+ module Client
3
+ class BodyCollection
4
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
5
+
6
+ def initialize(&load_with_proc)
7
+ raise ArgumentException, 'BodyCollection must be initialized with a block' unless block_given?
8
+
9
+ @loaded = false
10
+ @finished = false
11
+ @load_with_proc = load_with_proc
12
+ @callbacks = []
13
+ @array = []
14
+ end
15
+
16
+ def each(&block)
17
+ @callbacks << block
18
+ @array.each {|a| yield(*a) }
19
+
20
+ lazy_load unless finished?
21
+ ensure
22
+ @callbacks.delete(block)
23
+ end
24
+
25
+ def lazy_load
26
+ until finished?
27
+ @load_with_proc[self]
28
+ end
29
+ end
30
+
31
+ def <<(value)
32
+ @array << value
33
+ @callbacks.each {|cb| cb[value] }
34
+ end
35
+
36
+ def finished?
37
+ @finished
38
+ end
39
+
40
+ def finish
41
+ @finished = true
42
+ end
43
+
44
+ def method_missing(sym, *a, &b)
45
+ lazy_load
46
+ @array.send(sym, *a, &b)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,15 @@
1
+ module Rack
2
+ module Client
3
+ module Parser
4
+ class Context
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ Response.new(*@app.call(Request.new(env).env)).finish
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ require 'json'
2
+
3
+ module Rack
4
+ module Client
5
+ module Parser
6
+ class JSON < Parser::Base
7
+
8
+ content_type 'application', 'json'
9
+
10
+ def encode(input)
11
+ output = StringIO.new
12
+
13
+ input.each do |object|
14
+ ::JSON.dump(object, output)
15
+ end
16
+
17
+ output
18
+ end
19
+
20
+ def decode(body)
21
+ BodyCollection.new do |collection|
22
+ begin
23
+ data = if body.respond_to? :read
24
+ body.read
25
+ elsif body.respond_to? :to_path
26
+ File.read(body.to_path)
27
+ else
28
+ io = StringIO.new
29
+
30
+ body.each do |part|
31
+ io << part
32
+ end
33
+
34
+ io.rewind
35
+ io.read
36
+ end
37
+
38
+ case result = ::JSON.parse(data)
39
+ when Array then result.each {|object| collection << object }
40
+ else collection << result
41
+ end
42
+
43
+ collection.finish
44
+ ensure
45
+ body.close if body.respond_to? :close
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ Yaml = YAML
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,8 @@
1
+ module Rack
2
+ module Client
3
+ module Parser
4
+ class Middleware
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ module Rack
2
+ module Client
3
+ module Parser
4
+ class Request < Rack::Request
5
+ def initialize(*)
6
+ super
7
+
8
+ if @env['rack-client.body_collection'] && content_type
9
+ parse_input(@env['rack-client.body_collection'])
10
+ end
11
+ end
12
+
13
+ def parse_input(collection)
14
+ if parser = Base.lookup(content_type)
15
+ @env['rack.input'] = parser.new.encode(collection)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module Rack
2
+ module Client
3
+ module Parser
4
+ class Response < Rack::Client::Response
5
+ def finish(*)
6
+ super
7
+ ensure
8
+ parse_body_as(headers['Content-Type']) if headers['Content-Type']
9
+ end
10
+
11
+ def parse_body_as(content_type)
12
+ if parser = Base.lookup(content_type)
13
+ headers['rack-client.body_collection'] = parser.new.decode(body)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ require 'yaml'
2
+
3
+ module Rack
4
+ module Client
5
+ module Parser
6
+ class YAML < Parser::Base
7
+
8
+ content_type 'application', 'x-yaml'
9
+
10
+ def encode(input)
11
+ output = StringIO.new
12
+
13
+ input.each do |object|
14
+ ::YAML.dump(object, output)
15
+ end
16
+
17
+ output
18
+ end
19
+
20
+ def decode(body)
21
+ BodyCollection.new do |collection|
22
+ begin
23
+ io = if body.respond_to? :to_path
24
+ File.open(body.to_path, 'r')
25
+ else
26
+ io = StringIO.new
27
+
28
+ body.each do |part|
29
+ io << part
30
+ end
31
+
32
+ io.rewind
33
+ io
34
+ end
35
+
36
+ ::YAML.load_documents(io) do |object|
37
+ collection << object
38
+ end
39
+
40
+ collection.finish
41
+ ensure
42
+ io.close if io.respond_to? :close
43
+ body.close if body.respond_to? :close
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ Yaml = YAML
50
+ end
51
+ end
52
+ end