httpadapter 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,92 @@
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 'httpadapter'
16
+ require 'typhoeus'
17
+ require 'typhoeus/request'
18
+ require 'typhoeus/response'
19
+ require 'addressable/uri'
20
+
21
+ module HTTPAdapter #:nodoc:
22
+ class TyphoeusRequestAdapter
23
+ def initialize(request)
24
+ unless request.kind_of?(Typhoeus::Request)
25
+ raise TypeError, "Expected Typhoeus::Request, got #{request.class}."
26
+ end
27
+ @request = request
28
+ end
29
+
30
+ def to_ary
31
+ method = @request.method.to_s.upcase
32
+ uri = @request.url.to_str
33
+ headers = []
34
+ @request.headers.each do |header, value|
35
+ headers << [header, value]
36
+ end
37
+ body = @request.body || ""
38
+ return [method, uri, headers, [body]]
39
+ end
40
+
41
+ def self.from_ary(array)
42
+ method, uri, headers, body = array
43
+ method = method.to_s.downcase.to_sym
44
+ uri = Addressable::URI.parse(uri)
45
+ merged_body = ""
46
+ body.each do |chunk|
47
+ merged_body += chunk
48
+ end
49
+ request = Typhoeus::Request.new(
50
+ uri.to_str,
51
+ :method => method,
52
+ :headers => Hash[headers],
53
+ :body => merged_body
54
+ )
55
+ return request
56
+ end
57
+ end
58
+
59
+ class TyphoeusResponseAdapter
60
+ def initialize(response)
61
+ unless response.kind_of?(Typhoeus::Response)
62
+ raise TypeError, "Expected Typhoeus::Response, got #{response.class}."
63
+ end
64
+ @response = response
65
+ end
66
+
67
+ def to_ary
68
+ status = @response.code.to_i
69
+ headers = []
70
+ @response.headers_hash.each do |header, value|
71
+ headers << [header, value]
72
+ end
73
+ body = @response.body || ""
74
+ return [status, headers, [body]]
75
+ end
76
+
77
+ def self.from_ary(array)
78
+ status, headers, body = array
79
+ status = status.to_i
80
+ merged_body = ""
81
+ body.each do |chunk|
82
+ merged_body += chunk
83
+ end
84
+ response = Typhoeus::Response.new(
85
+ :code => status,
86
+ :headers => headers.inject('') { |a,(h,v)| a << "#{h}: #{v}\r\n"; a },
87
+ :body => merged_body
88
+ )
89
+ return response
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,26 @@
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
+ # Used to prevent the class/module from being loaded more than once
16
+ unless defined? HTTPAdapter::VERSION
17
+ module HTTPAdapter #:nodoc:
18
+ module VERSION #:nodoc:
19
+ MAJOR = 0
20
+ MINOR = 1
21
+ TINY = 1
22
+
23
+ STRING = [MAJOR, MINOR, TINY].join('.')
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,232 @@
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 "httpadapter/version"
16
+
17
+ module HTTPAdapter #:nodoc:
18
+ ##
19
+ # Converts an HTTP request object to a simple tuple.
20
+ #
21
+ # @param [Object, #to_ary] request
22
+ # The request object to be converted. The request may either implement
23
+ # the <code>#to_ary</code> method directly or alternately, an optional
24
+ # adapter class may be provided. The adapter must accept the request
25
+ # object as a parameter and provide the <code>#to_ary</code> method.
26
+ #
27
+ # @return [Array] The tuple that the request was converted to.
28
+ def self.adapt_request(request, adapter=nil)
29
+ # Temporarily wrap the request if there's an adapter
30
+ request = adapter.new(request) if adapter
31
+ if request.respond_to?(:to_ary)
32
+ converted_request = request.to_ary
33
+ else
34
+ # Can't use #to_a because some versions of Ruby define #to_a on Object
35
+ raise TypeError,
36
+ "Expected adapter or request to implement #to_ary."
37
+ end
38
+ return self.verified_request(converted_request)
39
+ end
40
+
41
+ ##
42
+ # Converts an HTTP response object to a simple tuple.
43
+ #
44
+ # @param [Object, #to_ary] response
45
+ # The response object to be converted. The response may either implement
46
+ # the <code>#to_ary</code> method directly or alternately, an optional
47
+ # adapter class may be provided. The adapter must accept the response
48
+ # object as a parameter and provide the <code>#to_ary</code> method.
49
+ #
50
+ # @return [Array] The tuple that the reponse was converted to.
51
+ def self.adapt_response(response, adapter=nil)
52
+ # Temporarily wrap the response if there's an adapter
53
+ response = adapter.new(response) if adapter
54
+ if response.respond_to?(:to_ary)
55
+ converted_response = response.to_ary
56
+ else
57
+ # Can't use #to_a because some versions of Ruby define #to_a on Object
58
+ raise TypeError,
59
+ "Expected adapter or response to implement #to_ary."
60
+ end
61
+ return self.verified_response(converted_response)
62
+ end
63
+
64
+ ##
65
+ # Converts a tuple to a specific HTTP implementation's request format.
66
+ #
67
+ # @param [Array] request
68
+ # The request object to be converted. The request object must be a tuple
69
+ # with a length of 4. The first element must be the request method.
70
+ # The second element must be the URI. The URI may be relative. The third
71
+ # element contains the headers. It must respond to <code>#each</code> and
72
+ # iterate over the header names and values. The fourth element must be
73
+ # the body. It must respond to <code>#each</code> but may not be a
74
+ # <code>String</code>. It should emit <code>String</code> objects.
75
+ # @param [#from_ary] adapter
76
+ # The adapter object that will convert to a tuple. It must respond to
77
+ # <code>#from_ary</code>. Typically a reference to a class is used.
78
+ #
79
+ # @return [Array] The implementation-specific request object.
80
+ def self.specialize_request(request, adapter)
81
+ request = self.verified_request(request)
82
+ if adapter.respond_to?(:from_ary)
83
+ return adapter.from_ary(request)
84
+ else
85
+ raise TypeError,
86
+ "Expected adapter to implement .from_ary."
87
+ end
88
+ end
89
+
90
+ ##
91
+ # Converts a tuple to a specific HTTP implementation's response format.
92
+ #
93
+ # @param [Array] response
94
+ # The response object to be converted. The response object must be a
95
+ # tuple with a length of 3. The first element must be the HTTP status
96
+ # code. The second element contains the headers. It must respond to
97
+ # <code>#each</code> and iterate over the header names and values. The
98
+ # third element must be the body. It must respond to <code>#each</code>
99
+ # but may not be a <code>String</code>. It should emit
100
+ # <code>String</code> objects. This is essentially the same format that
101
+ # Rack uses.
102
+ # @param [#from_ary] adapter
103
+ # The adapter object that will convert to a tuple. It must respond to
104
+ # <code>#from_ary</code>. Typically a reference to a class is used.
105
+ #
106
+ # @return [Array] The implementation-specific response object.
107
+ def self.specialize_response(response, adapter)
108
+ response = self.verified_response(response)
109
+ if adapter.respond_to?(:from_ary)
110
+ return adapter.from_ary(response)
111
+ else
112
+ raise TypeError,
113
+ "Expected adapter to implement .from_ary."
114
+ end
115
+ end
116
+
117
+ protected
118
+ ##
119
+ # Verifies a request tuple matches the specification.
120
+ #
121
+ # @param [Array] request
122
+ # The request object to be verified.
123
+ #
124
+ # @return [Array] The tuple, after normalization.
125
+ def self.verified_request(request)
126
+ if !request.kind_of?(Array)
127
+ raise TypeError, "Expected Array, got #{request.class}."
128
+ end
129
+ if request.size == 4
130
+ # Verify that the request object matches the specification
131
+ method, uri, headers, body = request
132
+ method = method.to_str if method.respond_to?(:to_str)
133
+ # Special-casing symbols here
134
+ method = method.to_s if method.kind_of?(Symbol)
135
+ if !method.kind_of?(String)
136
+ raise TypeError,
137
+ "Expected String, got #{method.class}."
138
+ end
139
+ method = method.upcase
140
+ if uri.respond_to?(:to_str)
141
+ uri = uri.to_str
142
+ else
143
+ raise TypeError, "Expected String, got #{uri.class}."
144
+ end
145
+ original_headers, headers = headers, []
146
+ if original_headers.respond_to?(:each)
147
+ original_headers.each do |header, value|
148
+ if header.respond_to?(:to_str)
149
+ header = header.to_str
150
+ else
151
+ raise TypeError, "Expected String, got #{header.class}."
152
+ end
153
+ if value.respond_to?(:to_str)
154
+ value = value.to_str
155
+ else
156
+ raise TypeError, "Expected String, got #{value.class}."
157
+ end
158
+ headers << [header, value]
159
+ end
160
+ else
161
+ raise TypeError, "Expected headers to respond to #each."
162
+ end
163
+ if body.kind_of?(String)
164
+ raise TypeError,
165
+ 'Body must not be a String; it must respond to #each and ' +
166
+ 'emit String values.'
167
+ end
168
+ # Can't verify that all chunks are Strings because #each may be
169
+ # effectively destructive.
170
+ if !body.respond_to?(:each)
171
+ raise TypeError, "Expected body to respond to #each."
172
+ end
173
+ else
174
+ raise TypeError,
175
+ "Expected tuple of [method, uri, headers, body]."
176
+ end
177
+ return [method, uri, headers, body]
178
+ end
179
+
180
+ ##
181
+ # Verifies a response tuple matches the specification.
182
+ #
183
+ # @param [Array] response
184
+ # The response object to be verified.
185
+ #
186
+ # @return [Array] The tuple, after normalization.
187
+ def self.verified_response(response)
188
+ if !response.kind_of?(Array)
189
+ raise TypeError, "Expected Array, got #{response.class}."
190
+ end
191
+ if response.size == 3
192
+ # Verify that the response object matches the specification
193
+ status, headers, body = response
194
+ status = status.to_i if status.respond_to?(:to_i)
195
+ if !status.kind_of?(Integer)
196
+ raise TypeError, "Expected Integer, got #{status.class}."
197
+ end
198
+ original_headers, headers = headers, []
199
+ if original_headers.respond_to?(:each)
200
+ original_headers.each do |header, value|
201
+ if header.respond_to?(:to_str)
202
+ header = header.to_str
203
+ else
204
+ raise TypeError, "Expected String, got #{header.class}."
205
+ end
206
+ if value.respond_to?(:to_str)
207
+ value = value.to_str
208
+ else
209
+ raise TypeError, "Expected String, got #{value.class}."
210
+ end
211
+ headers << [header, value]
212
+ end
213
+ else
214
+ raise TypeError, "Expected headers to respond to #each."
215
+ end
216
+ if body.kind_of?(String)
217
+ raise TypeError,
218
+ 'Body must not be a String; it must respond to #each and ' +
219
+ 'emit String values.'
220
+ end
221
+ # Can't verify that all chunks are Strings because #each may be
222
+ # effectively destructive.
223
+ if !body.respond_to?(:each)
224
+ raise TypeError, "Expected body to respond to #each."
225
+ end
226
+ else
227
+ raise TypeError,
228
+ "Expected tuple of [status, headers, body]."
229
+ end
230
+ return [status, headers, body]
231
+ end
232
+ end