httpadapter 0.2.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/README.md +48 -0
- data/lib/httpadapter.rb +87 -75
- data/lib/httpadapter/adapters/mock.rb +9 -6
- data/lib/httpadapter/adapters/net_http.rb +125 -121
- data/lib/httpadapter/adapters/rack.rb +29 -31
- data/lib/httpadapter/adapters/typhoeus.rb +46 -50
- data/lib/httpadapter/connection.rb +0 -2
- data/lib/httpadapter/version.rb +3 -3
- data/spec/httpadapter/adapter_type_checking_spec.rb +170 -0
- data/spec/httpadapter/adapters/mock_adapter_spec.rb +81 -0
- data/spec/httpadapter/adapters/net_http_spec.rb +633 -0
- data/spec/httpadapter/adapters/rack_spec.rb +357 -0
- data/spec/httpadapter/adapters/typhoeus_spec.rb +379 -0
- data/spec/httpadapter_spec.rb +59 -180
- data/tasks/gem.rake +2 -2
- data/tasks/rdoc.rake +1 -1
- data/tasks/spec.rake +4 -1
- data/tasks/yard.rake +1 -1
- metadata +15 -18
- data/README +0 -49
- data/spec/httpadapter/adapters/net_http_request_spec.rb +0 -417
- data/spec/httpadapter/adapters/net_http_response_spec.rb +0 -170
- data/spec/httpadapter/adapters/net_http_transmission_spec.rb +0 -130
- data/spec/httpadapter/adapters/rack_request_spec.rb +0 -239
- data/spec/httpadapter/adapters/rack_response_spec.rb +0 -158
- data/spec/httpadapter/adapters/typhoeus_request_spec.rb +0 -203
- data/spec/httpadapter/adapters/typhoeus_response_spec.rb +0 -146
- data/spec/httpadapter/adapters/typhoeus_transmission_spec.rb +0 -89
@@ -19,20 +19,24 @@ require 'rack/response'
|
|
19
19
|
require 'addressable/uri'
|
20
20
|
|
21
21
|
module HTTPAdapter #:nodoc:
|
22
|
-
class
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
class RackAdapter
|
23
|
+
include HTTPAdapter
|
24
|
+
|
25
|
+
def initialize(options={})
|
26
|
+
options = {
|
27
|
+
:error_stream => $stderr
|
28
|
+
}.merge(options)
|
29
|
+
@error_stream = options[:error_stream]
|
29
30
|
end
|
30
31
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
32
|
+
def convert_request_to_a(request_obj)
|
33
|
+
unless request_obj.kind_of?(Rack::Request)
|
34
|
+
raise TypeError, "Expected Rack::Request, got #{request_obj.class}."
|
35
|
+
end
|
36
|
+
method = request_obj.request_method.to_s.upcase
|
37
|
+
uri = Addressable::URI.parse(request_obj.url.to_s).normalize.to_s
|
34
38
|
headers = []
|
35
|
-
|
39
|
+
request_obj.env.each do |parameter, value|
|
36
40
|
next if parameter !~ /^HTTP_/
|
37
41
|
# Ugh, lossy canonicalization again
|
38
42
|
header = (parameter.gsub(/^HTTP_/, '').split('_').map do |chunk|
|
@@ -40,12 +44,12 @@ module HTTPAdapter #:nodoc:
|
|
40
44
|
end).join('-')
|
41
45
|
headers << [header, value]
|
42
46
|
end
|
43
|
-
return [method, uri, headers,
|
47
|
+
return [method, uri, headers, request_obj.body]
|
44
48
|
end
|
45
49
|
|
46
|
-
def
|
50
|
+
def convert_request_from_a(request_ary)
|
47
51
|
# These contortions are really obnoxious; lossiness is bad!
|
48
|
-
method, uri, headers, body =
|
52
|
+
method, uri, headers, body = request_ary
|
49
53
|
env = {}
|
50
54
|
method = method.to_s.upcase
|
51
55
|
uri = Addressable::URI.parse(uri)
|
@@ -88,26 +92,15 @@ module HTTPAdapter #:nodoc:
|
|
88
92
|
return request
|
89
93
|
end
|
90
94
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
class RackResponseAdapter
|
98
|
-
def initialize(response)
|
99
|
-
unless response.kind_of?(Rack::Response)
|
100
|
-
raise TypeError, "Expected Rack::Response, got #{response.class}."
|
95
|
+
def convert_response_to_a(response_obj)
|
96
|
+
unless response_obj.kind_of?(Rack::Response)
|
97
|
+
raise TypeError, "Expected Rack::Response, got #{response_obj.class}."
|
101
98
|
end
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
def to_ary
|
106
|
-
return @response.finish
|
99
|
+
return response_obj.finish
|
107
100
|
end
|
108
101
|
|
109
|
-
def
|
110
|
-
status, headers, body =
|
102
|
+
def convert_response_from_a(request_ary)
|
103
|
+
status, headers, body = request_ary
|
111
104
|
status = status.to_i
|
112
105
|
body.each do |chunk|
|
113
106
|
# Purely for strict type-checking
|
@@ -118,5 +111,10 @@ module HTTPAdapter #:nodoc:
|
|
118
111
|
response = Rack::Response.new(body, status, Hash[headers])
|
119
112
|
return response
|
120
113
|
end
|
114
|
+
|
115
|
+
def fetch_resource(request_ary, connection=nil)
|
116
|
+
raise NotImplementedError,
|
117
|
+
'No HTTP client implementation available to transmit a Rack::Request.'
|
118
|
+
end
|
121
119
|
end
|
122
120
|
end
|
@@ -19,27 +19,26 @@ require 'typhoeus/response'
|
|
19
19
|
require 'addressable/uri'
|
20
20
|
|
21
21
|
module HTTPAdapter #:nodoc:
|
22
|
-
class
|
23
|
-
|
24
|
-
unless request.kind_of?(Typhoeus::Request)
|
25
|
-
raise TypeError, "Expected Typhoeus::Request, got #{request.class}."
|
26
|
-
end
|
27
|
-
@request = request
|
28
|
-
end
|
22
|
+
class TyphoeusAdapter
|
23
|
+
include HTTPAdapter
|
29
24
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
25
|
+
def convert_request_to_a(request_obj)
|
26
|
+
unless request_obj.kind_of?(Typhoeus::Request)
|
27
|
+
raise TypeError,
|
28
|
+
"Expected Typhoeus::Request, got #{request_obj.class}."
|
29
|
+
end
|
30
|
+
method = request_obj.method.to_s.upcase
|
31
|
+
uri = request_obj.url.to_str
|
33
32
|
headers = []
|
34
|
-
|
33
|
+
request_obj.headers.each do |header, value|
|
35
34
|
headers << [header, value]
|
36
35
|
end
|
37
|
-
body =
|
36
|
+
body = request_obj.body || ""
|
38
37
|
return [method, uri, headers, [body]]
|
39
38
|
end
|
40
39
|
|
41
|
-
def
|
42
|
-
method, uri, headers, body =
|
40
|
+
def convert_request_from_a(request_ary)
|
41
|
+
method, uri, headers, body = request_ary
|
43
42
|
method = method.to_s.downcase.to_sym
|
44
43
|
uri = Addressable::URI.parse(uri)
|
45
44
|
headers = Hash[headers]
|
@@ -59,41 +58,14 @@ module HTTPAdapter #:nodoc:
|
|
59
58
|
return request
|
60
59
|
end
|
61
60
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
typhoeus_response = nil
|
67
|
-
unless connection
|
68
|
-
hydra = Typhoeus::Hydra.new
|
69
|
-
connection = HTTPAdapter::Connection.new(
|
70
|
-
uri.host, uri.inferred_port, hydra,
|
71
|
-
:join => [:run, [], nil]
|
72
|
-
)
|
73
|
-
else
|
74
|
-
http = nil
|
75
|
-
end
|
76
|
-
typhoeus_request.on_complete do |response|
|
77
|
-
typhoeus_response = response
|
78
|
-
end
|
79
|
-
connection.connection.queue(typhoeus_request)
|
80
|
-
connection.join
|
81
|
-
return TyphoeusResponseAdapter.new(typhoeus_response).to_ary
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
class TyphoeusResponseAdapter
|
86
|
-
def initialize(response)
|
87
|
-
unless response.kind_of?(Typhoeus::Response)
|
88
|
-
raise TypeError, "Expected Typhoeus::Response, got #{response.class}."
|
61
|
+
def convert_response_to_a(response_obj)
|
62
|
+
unless response_obj.kind_of?(Typhoeus::Response)
|
63
|
+
raise TypeError,
|
64
|
+
"Expected Typhoeus::Response, got #{response_obj.class}."
|
89
65
|
end
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
|
-
def to_ary
|
94
|
-
status = @response.code.to_i
|
66
|
+
status = response_obj.code.to_i
|
95
67
|
headers = []
|
96
|
-
|
68
|
+
response_obj.headers_hash.each do |header, value|
|
97
69
|
# Eh? Seriously? This is NOT a header!
|
98
70
|
next if header =~ /^HTTP\/\d\.\d \d{3} .+$/
|
99
71
|
if value.kind_of?(Array)
|
@@ -105,12 +77,12 @@ module HTTPAdapter #:nodoc:
|
|
105
77
|
headers << [header, value]
|
106
78
|
end
|
107
79
|
end
|
108
|
-
body =
|
80
|
+
body = response_obj.body || ""
|
109
81
|
return [status, headers, [body]]
|
110
82
|
end
|
111
83
|
|
112
|
-
def
|
113
|
-
status, headers, body =
|
84
|
+
def convert_response_from_a(request_ary)
|
85
|
+
status, headers, body = request_ary
|
114
86
|
status = status.to_i
|
115
87
|
merged_body = ""
|
116
88
|
body.each do |chunk|
|
@@ -123,5 +95,29 @@ module HTTPAdapter #:nodoc:
|
|
123
95
|
)
|
124
96
|
return response
|
125
97
|
end
|
98
|
+
|
99
|
+
def fetch_resource(request_ary, connection=nil)
|
100
|
+
method, uri, headers, body = request_ary
|
101
|
+
uri = Addressable::URI.parse(uri)
|
102
|
+
typhoeus_request = self.convert_request_from_a(
|
103
|
+
[method, uri, headers, body]
|
104
|
+
)
|
105
|
+
typhoeus_response = nil
|
106
|
+
unless connection
|
107
|
+
hydra = Typhoeus::Hydra.new
|
108
|
+
connection = HTTPAdapter::Connection.new(
|
109
|
+
uri.host, uri.inferred_port, hydra,
|
110
|
+
:join => [:run, [], nil]
|
111
|
+
)
|
112
|
+
else
|
113
|
+
http = nil
|
114
|
+
end
|
115
|
+
typhoeus_request.on_complete do |response|
|
116
|
+
typhoeus_response = response
|
117
|
+
end
|
118
|
+
connection.connection.queue(typhoeus_request)
|
119
|
+
connection.join
|
120
|
+
return self.convert_response_to_a(typhoeus_response)
|
121
|
+
end
|
126
122
|
end
|
127
123
|
end
|
data/lib/httpadapter/version.rb
CHANGED
@@ -0,0 +1,170 @@
|
|
1
|
+
# Copyright (C) 2010 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
shared_examples_for 'adapter type-checking example' do
|
16
|
+
before do
|
17
|
+
@request = ['GET', '/', [], ['']]
|
18
|
+
@response = [200, [], ['']]
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'when attempting to specialize a request' do
|
22
|
+
it 'should raise an error for converting from an invalid tuple' do
|
23
|
+
(lambda do
|
24
|
+
@adapter.specialize_request(42)
|
25
|
+
end).should raise_error(TypeError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should raise an error for converting from an invalid tuple' do
|
29
|
+
(lambda do
|
30
|
+
@adapter.specialize_request([42])
|
31
|
+
end).should raise_error(TypeError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should raise an error for converting from an invalid tuple' do
|
35
|
+
(lambda do
|
36
|
+
@adapter.specialize_request([42, 42, 42, 42])
|
37
|
+
end).should raise_error(TypeError)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should raise an error for converting from an invalid tuple' do
|
41
|
+
(lambda do
|
42
|
+
@adapter.specialize_request(['GET', 42, [], ['']])
|
43
|
+
end).should raise_error(TypeError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should raise an error for converting from an invalid tuple' do
|
47
|
+
(lambda do
|
48
|
+
@adapter.specialize_request(['GET', '/', 42, ['']])
|
49
|
+
end).should raise_error(TypeError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should raise an error for converting from an invalid tuple' do
|
53
|
+
(lambda do
|
54
|
+
@adapter.specialize_request(['GET', '/', [42], ['']])
|
55
|
+
end).should raise_error(TypeError)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should raise an error for converting from an invalid tuple' do
|
59
|
+
(lambda do
|
60
|
+
@adapter.specialize_request(['GET', '/', [[42, 'value']], ['']])
|
61
|
+
end).should raise_error(TypeError)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should raise an error for converting from an invalid tuple' do
|
65
|
+
(lambda do
|
66
|
+
@adapter.specialize_request(['GET', '/', [['X', 42]], ['']])
|
67
|
+
end).should raise_error(TypeError)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should raise an error for converting from an invalid tuple' do
|
71
|
+
(lambda do
|
72
|
+
@adapter.specialize_request(['GET', '/', [], 42])
|
73
|
+
end).should raise_error(TypeError)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should raise an error for converting from an invalid tuple' do
|
77
|
+
(lambda do
|
78
|
+
# Note that the body value here should be [''], not ''.
|
79
|
+
@adapter.specialize_request(['GET', '/', [], ''])
|
80
|
+
end).should raise_error(TypeError)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe 'when attempting to specialize a response' do
|
85
|
+
it 'should raise an error for converting from an invalid tuple' do
|
86
|
+
(lambda do
|
87
|
+
@adapter.specialize_response(42)
|
88
|
+
end).should raise_error(TypeError)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should raise an error for converting from an invalid tuple' do
|
92
|
+
(lambda do
|
93
|
+
@adapter.specialize_response([42])
|
94
|
+
end).should raise_error(TypeError)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should raise an error for converting from an invalid tuple' do
|
98
|
+
(lambda do
|
99
|
+
@adapter.specialize_response([42, 42, 42])
|
100
|
+
end).should raise_error(TypeError)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should raise an error for converting from an invalid tuple' do
|
104
|
+
(lambda do
|
105
|
+
@adapter.specialize_response([Object.new, [], ['']])
|
106
|
+
end).should raise_error(TypeError)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should raise an error for converting from an invalid tuple' do
|
110
|
+
(lambda do
|
111
|
+
@adapter.specialize_response(['', 42, ['']])
|
112
|
+
end).should raise_error(TypeError)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should raise an error for converting from an invalid tuple' do
|
116
|
+
(lambda do
|
117
|
+
@adapter.specialize_response([200, [42], ['']])
|
118
|
+
end).should raise_error(TypeError)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should raise an error for converting from an invalid tuple' do
|
122
|
+
(lambda do
|
123
|
+
@adapter.specialize_response([200, [[42, 'value']], ['']])
|
124
|
+
end).should raise_error(TypeError)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should raise an error for converting from an invalid tuple' do
|
128
|
+
(lambda do
|
129
|
+
@adapter.specialize_response([200, [['X', 42]], ['']])
|
130
|
+
end).should raise_error(TypeError)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should raise an error for converting from an invalid tuple' do
|
134
|
+
(lambda do
|
135
|
+
@adapter.specialize_response([200, [], 42])
|
136
|
+
end).should raise_error(TypeError)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should raise an error for converting from an invalid tuple' do
|
140
|
+
(lambda do
|
141
|
+
# Note that the body value here should be [''], not ''.
|
142
|
+
@adapter.specialize_response([200, [], ''])
|
143
|
+
end).should raise_error(TypeError)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should raise an error for invalid adapt request calls' do
|
148
|
+
(lambda do
|
149
|
+
@adapter.adapt_request(42)
|
150
|
+
end).should raise_error(TypeError)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should raise an error for invalid adapt response calls' do
|
154
|
+
(lambda do
|
155
|
+
@adapter.adapt_response(42)
|
156
|
+
end).should raise_error(TypeError)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should raise an error for invalid transmission calls' do
|
160
|
+
(lambda do
|
161
|
+
@adapter.transmit(42)
|
162
|
+
end).should raise_error(TypeError)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should raise an error for invalid transmission calls' do
|
166
|
+
(lambda do
|
167
|
+
@adapter.transmit(@request, 42)
|
168
|
+
end).should raise_error(TypeError)
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# Copyright (C) 2010 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'spec_helper'
|
16
|
+
require 'spec/httpadapter/adapter_type_checking_spec'
|
17
|
+
|
18
|
+
require 'httpadapter/adapters/mock'
|
19
|
+
|
20
|
+
describe HTTPAdapter::MockAdapter do
|
21
|
+
describe 'with no response mocked' do
|
22
|
+
before do
|
23
|
+
@adapter = HTTPAdapter::MockAdapter.create do |request_ary, connection|
|
24
|
+
method, uri, headers, body = request_ary
|
25
|
+
headers.should be_any { |k, v| k.downcase == 'user-agent' }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it_should_behave_like 'adapter type-checking example'
|
30
|
+
|
31
|
+
describe 'when transmitting a request' do
|
32
|
+
it 'should have an expectation failure' do
|
33
|
+
(lambda do
|
34
|
+
@adapter.transmit(['GET', 'http://www.google.com/', [], ['']])
|
35
|
+
end).should raise_error(Spec::Expectations::ExpectationNotMetError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should meet all expectations' do
|
39
|
+
response = @adapter.transmit([
|
40
|
+
'GET',
|
41
|
+
'http://www.google.com/',
|
42
|
+
[['User-Agent', 'Mock Agent']],
|
43
|
+
['']
|
44
|
+
])
|
45
|
+
status, headers, body = response
|
46
|
+
status.should == 200
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'with a mocked response' do
|
52
|
+
before do
|
53
|
+
@adapter = HTTPAdapter::MockAdapter.create do |request_ary, connection|
|
54
|
+
method, uri, headers, body = request_ary
|
55
|
+
headers.should be_any { |k, v| k.downcase == 'user-agent' }
|
56
|
+
[400, [], ['']]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it_should_behave_like 'adapter type-checking example'
|
61
|
+
|
62
|
+
describe 'when transmitting a request' do
|
63
|
+
it 'should have an expectation failure' do
|
64
|
+
(lambda do
|
65
|
+
@adapter.transmit(['GET', 'http://www.google.com/', [], ['']])
|
66
|
+
end).should raise_error(Spec::Expectations::ExpectationNotMetError)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should meet all expectations' do
|
70
|
+
response = @adapter.transmit([
|
71
|
+
'GET',
|
72
|
+
'http://www.google.com/',
|
73
|
+
[['User-Agent', 'Mock Agent']],
|
74
|
+
['']
|
75
|
+
])
|
76
|
+
status, headers, body = response
|
77
|
+
status.should == 400
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|