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

Sign up to get free protection for your applications and to get access to all the features.
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.