rets 0.3.0.rc.1 → 0.3.0.rc.2

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.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### 0.3.0.rc.1
2
+
3
+ * correctly handle digest authentication
4
+
1
5
  ### 0.3.0.rc.0 / 2012-07-26
2
6
 
3
7
  * feature: significantly better handling of authorization failures
data/Rakefile CHANGED
@@ -3,6 +3,7 @@ require 'hoe'
3
3
 
4
4
  Hoe.plugin :git, :doofus
5
5
  Hoe.plugin :travis
6
+ Hoe.plugin :gemspec
6
7
 
7
8
  Hoe.spec 'rets' do
8
9
  developer 'Estately, Inc. Open Source', 'opensource@estately.com'
data/lib/rets/client.rb CHANGED
@@ -44,7 +44,8 @@ module Rets
44
44
  # provided in initialize. Returns the capabilities that the
45
45
  # RETS server provides, per http://retsdoc.onconfluence.com/display/rets172/4.10+Capability+URL+List.
46
46
  def login
47
- request(uri.path)
47
+ response = request(uri.path)
48
+ self.capabilities = extract_capabilities(Nokogiri.parse(response.body))
48
49
  raise UnknownResponse, "Cannot read rets server capabilities." unless @capabilities
49
50
  @capabilities
50
51
  end
@@ -94,7 +95,7 @@ module Rets
94
95
  end
95
96
  end
96
97
  end
97
-
98
+
98
99
  def find_every(opts = {})
99
100
  search_uri = capability_url("Search")
100
101
 
@@ -235,9 +236,12 @@ module Rets
235
236
  def metadata
236
237
  return @metadata if @metadata
237
238
 
238
- if @cached_metadata && @cached_metadata.current?(capabilities["MetadataTimestamp"], capabilities["MetadataVersion"])
239
+ if @cached_metadata && (@options[:skip_metadata_uptodate_check] ||
240
+ @cached_metadata.current?(capabilities["MetadataTimestamp"], capabilities["MetadataVersion"]))
241
+ logger.info "Use cached metadata"
239
242
  self.metadata = @cached_metadata
240
243
  else
244
+ logger.info @cached_metadata ? "Cached metadata out of date" : "Cached metadata unavailable"
241
245
  metadata_fetcher = lambda { |type| retrieve_metadata_type(type) }
242
246
  self.metadata = Metadata::Root.new(&metadata_fetcher)
243
247
  end
@@ -263,15 +267,18 @@ module Rets
263
267
  end
264
268
 
265
269
  def raw_request(path, body = nil, extra_headers = {}, &reader)
266
- logger.info "posting to #{path}"
267
270
  headers = build_headers.merge(extra_headers)
268
271
 
269
272
  post = Net::HTTP::Post.new(path, headers)
270
273
  post.body = body.to_s
271
274
 
272
- logger.debug ""
273
- logger.debug format_headers(headers)
274
- logger.debug body.to_s
275
+ logger.debug <<EOF
276
+ >>>> Request
277
+ POST #{path}
278
+ #{format_headers(headers)}
279
+
280
+ #{binary?(body.to_s) ? '<<< BINARY BODY >>>' : body.to_s}
281
+ EOF
275
282
 
276
283
  connection_args = [Net::HTTP::Persistent === connection ? uri : nil, post].compact
277
284
 
@@ -279,27 +286,39 @@ module Rets
279
286
  res.read_body(&reader)
280
287
  end
281
288
 
282
- handle_cookies(response)
289
+ logger.debug <<EOF
290
+ <<<< Response
291
+ #{response.code} #{response.message}
292
+ #{format_headers(response.to_hash)}
283
293
 
284
- logger.debug "Response: (#{response.class})"
285
- logger.debug ""
286
- logger.debug format_headers(response.to_hash)
287
- logger.debug ""
288
- logger.debug "Body:"
289
- logger.debug response.body
294
+ #{binary?(response.body.to_s) ? '<<< BINARY BODY >>>' : response.body.to_s}
295
+ EOF
290
296
 
297
+ handle_cookies(response)
291
298
  return response
292
299
  end
293
300
 
294
- def request(*args, &block)
295
- response = handle_response(raw_request(*args, &block))
301
+ def digest_auth_request(path, body = nil, extra_headers = {}, &reader)
302
+ response = raw_request(path, body, extra_headers, &reader)
296
303
  if Net::HTTPUnauthorized === response
297
- retry_response = handle_response(raw_request(*args, &block))
298
- raise AuthorizationFailure if retry_response === Net::HTTPUnauthorized
299
- retry_response
300
- else
301
- response
304
+ challenge = extract_digest_header(response)
305
+ if challenge
306
+ uri2 = URI.parse(uri.to_s)
307
+ uri2.user = uri.user
308
+ uri2.password = uri.password
309
+ uri2.path = path
310
+ self.authorization = build_auth(challenge, uri2, tries)
311
+ response = raw_request(path, body, extra_headers, &reader)
312
+ if Net::HTTPUnauthorized === response
313
+ raise AuthorizationFailure, "Authorization failed, check credentials?"
314
+ end
315
+ end
302
316
  end
317
+ response
318
+ end
319
+
320
+ def request(*args, &block)
321
+ handle_response(digest_auth_request(*args, &block))
303
322
  end
304
323
 
305
324
  def request_with_compact_response(path, body, headers)
@@ -310,38 +329,20 @@ module Rets
310
329
 
311
330
  def extract_digest_header(response)
312
331
  authenticate_headers = response.get_fields("www-authenticate")
313
- authenticate_headers.detect {|h| h =~ /Digest/}
314
- end
315
-
316
- def handle_unauthorized_response(response)
317
- if self.authorization.nil?
318
- self.authorization = build_auth(extract_digest_header(response), uri, tries)
319
- response = raw_request(uri.path)
320
-
321
- if Net::HTTPUnauthorized === response
322
- raise AuthorizationFailure, "Authorization failed, check credentials?"
323
- else
324
- ErrorChecker.check(response)
325
- self.capabilities = extract_capabilities(Nokogiri.parse(response.body))
326
- end
332
+ if authenticate_headers
333
+ authenticate_headers.detect {|h| h =~ /Digest/}
327
334
  else
328
- clean_setup
329
- login
335
+ nil
330
336
  end
331
337
  end
332
338
 
333
339
  def handle_response(response)
334
-
335
- if Net::HTTPUnauthorized === response # 401
336
- handle_unauthorized_response(response)
337
-
338
- elsif Net::HTTPSuccess === response # 2xx
340
+ if Net::HTTPSuccess === response
339
341
  ErrorChecker.check(response)
340
342
  else
341
343
  raise UnknownResponse, "Unable to handle response #{response.class}"
342
344
  end
343
-
344
- return response
345
+ response
345
346
  end
346
347
 
347
348
  def handle_cookies(response)
@@ -379,7 +380,6 @@ module Rets
379
380
  @cookies[name]
380
381
  end
381
382
 
382
-
383
383
  def session=(session)
384
384
  self.authorization = session.authorization
385
385
  self.capabilities = session.capabilities
@@ -478,7 +478,9 @@ module Rets
478
478
  data.map{|k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join("&")
479
479
  end
480
480
 
481
-
481
+ def binary?(data)
482
+ data.slice(0, 1024).any? {|b| b >= "\x0" && b < " " && b != '-' && b != '~' && b != "\t" && b != "\r" && b != "\n"}
483
+ end
482
484
 
483
485
  def tries
484
486
  @tries ||= 1
data/test/test_client.rb CHANGED
@@ -98,15 +98,6 @@ class TestClient < Test::Unit::TestCase
98
98
  client.request("/foo")
99
99
  end
100
100
 
101
-
102
- def test_handle_response_instigates_login_process
103
- response = Net::HTTPUnauthorized.new("","","")
104
-
105
- @client.expects(:handle_unauthorized_response)
106
-
107
- assert_equal response, @client.handle_response(response)
108
- end
109
-
110
101
  def test_handle_response_handles_rets_errors
111
102
  response = Net::HTTPSuccess.new("", "", "")
112
103
  response.stubs(:body => RETS_ERROR)
@@ -116,18 +107,6 @@ class TestClient < Test::Unit::TestCase
116
107
  end
117
108
  end
118
109
 
119
- def test_handle_unauthorize_response_handles_rets_errors
120
- response = Net::HTTPSuccess.new("", "", "")
121
- response.stubs(:body => RETS_ERROR)
122
- @client.stubs(:build_auth)
123
- @client.stubs(:extract_digest_header)
124
- @client.stubs(:raw_request).returns(response)
125
-
126
- assert_raise Rets::InvalidRequest do
127
- @client.handle_unauthorized_response(response)
128
- end
129
- end
130
-
131
110
  def test_handle_response_handles_rets_valid_response
132
111
  response = Net::HTTPSuccess.new("", "", "")
133
112
  response.stubs(:body => RETS_REPLY)
@@ -157,35 +136,6 @@ class TestClient < Test::Unit::TestCase
157
136
  end
158
137
  end
159
138
 
160
-
161
- def test_handle_unauthorized_response_sets_capabilities_on_success
162
- response = Net::HTTPSuccess.new("","","")
163
- response.stubs(:body => CAPABILITIES, :get_fields => ["xxx"])
164
-
165
- @client.stubs(:build_auth)
166
- @client.expects(:raw_request).with("/login").returns(response)
167
-
168
- @client.handle_unauthorized_response(response)
169
-
170
- capabilities = {"abc" => "123", "def" => "ghi=jk"}
171
-
172
- assert_equal capabilities, @client.capabilities
173
- end
174
-
175
- def test_handle_unauthorized_response_raises_on_auth_failure
176
- response = Net::HTTPUnauthorized.new("","","")
177
- response.stubs(:body => "", :get_fields => ["xxx"])
178
-
179
- @client.stubs(:build_auth)
180
- @client.expects(:raw_request).with("/login").returns(response)
181
-
182
- assert_raise Rets::AuthorizationFailure do
183
- @client.handle_unauthorized_response(response)
184
- end
185
- end
186
-
187
-
188
-
189
139
  def test_extract_capabilities
190
140
  assert_equal(
191
141
  {"abc" => "123", "def" => "ghi=jk"},
@@ -220,7 +170,10 @@ class TestClient < Test::Unit::TestCase
220
170
  end
221
171
 
222
172
  def test_login_fails_if_cannot_read_capabilities
223
- @client.stubs(:request)
173
+ response = Net::HTTPSuccess.new("", "", "")
174
+ response.stubs(:body => RETS_REPLY)
175
+ @client.stubs(:request).returns(response)
176
+ @client.stubs(:extract_capabilities)
224
177
  assert_raise Rets::UnknownResponse do
225
178
  @client.login
226
179
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rets
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.rc.1
4
+ version: 0.3.0.rc.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-26 00:00:00.000000000 Z
12
+ date: 2012-08-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-http-persistent
16
- requirement: &70186895334000 !ruby/object:Gem::Requirement
16
+ requirement: &70154418461060 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.7'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70186895334000
24
+ version_requirements: *70154418461060
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: nokogiri
27
- requirement: &70186895333580 !ruby/object:Gem::Requirement
27
+ requirement: &70154418460640 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.5.2
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70186895333580
35
+ version_requirements: *70154418460640
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: mocha
38
- requirement: &70186895333160 !ruby/object:Gem::Requirement
38
+ requirement: &70154418460220 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.11.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70186895333160
46
+ version_requirements: *70154418460220
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: vcr
49
- requirement: &70186895332740 !ruby/object:Gem::Requirement
49
+ requirement: &70154418459800 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 2.2.2
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70186895332740
57
+ version_requirements: *70154418459800
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: webmock
60
- requirement: &70186895332320 !ruby/object:Gem::Requirement
60
+ requirement: &70154418459380 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 1.8.0
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70186895332320
68
+ version_requirements: *70154418459380
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rdoc
71
- requirement: &70186895331900 !ruby/object:Gem::Requirement
71
+ requirement: &70154418458960 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '3.10'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70186895331900
79
+ version_requirements: *70154418458960
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: hoe
82
- requirement: &70186895331480 !ruby/object:Gem::Requirement
82
+ requirement: &70154418458540 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,7 +87,7 @@ dependencies:
87
87
  version: '2.13'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70186895331480
90
+ version_requirements: *70154418458540
91
91
  description: ! '[![Build Status](https://secure.travis-ci.org/estately/rets.png?branch=master)](http://travis-ci.org/estately/rets)
92
92
 
93
93
  A pure-ruby library for fetching data from [RETS] servers.