httpadapter 0.2.1 → 1.0.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.
- 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
|