rufus-jig 0.1.21 → 0.1.22

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.
@@ -2,7 +2,15 @@
2
2
  = rufus-jig CHANGELOG.txt
3
3
 
4
4
 
5
- == rufus-jig - 0.1.21 release 2010/09/21
5
+ == rufus-jig - 0.1.22 released 2010/09/30
6
+
7
+ - close methods are back
8
+ - basic authentication added
9
+ - net-http-persistent integration
10
+ - Rufus::Jig.parse_uri(s), uri now has scheme
11
+
12
+
13
+ == rufus-jig - 0.1.21 released 2010/09/21
6
14
 
7
15
  - fixed port.to_i minor issue
8
16
  - caching is only for GET !
@@ -11,6 +11,11 @@ This library also contains some CouchDB helpers.
11
11
  To select a HTTP transport layer for rufus-jig, just make sure you have loaded
12
12
  the library before loading rufus-jig.
13
13
 
14
+ For net-http-persistent:
15
+
16
+ require 'net/http/persistent'
17
+ require 'rufus/jig'
18
+
14
19
  For Patron:
15
20
 
16
21
  require 'patron'
@@ -33,7 +38,7 @@ Let's say we have an hypothetical document server.
33
38
 
34
39
  getting...
35
40
 
36
- require 'patron'
41
+ require 'net/http/persistent'
37
42
  require 'yajl'
38
43
  require 'rufus/jig'
39
44
 
@@ -49,8 +54,6 @@ getting...
49
54
  p [ r.status, r.body ]
50
55
  #
51
56
  # => [ 200, "letter:\n\nfour birds" ]
52
- #
53
- # else returns a Response object (currently the one of Patron)
54
57
 
55
58
  j = @h.get('/document', :content_type => 'application/json')
56
59
  j = @h.get('/document', :content_type => :json)
@@ -77,7 +80,7 @@ The class Rufus::Jig::Couch provides a get/put/delete trio that is couch-oriente
77
80
 
78
81
  put and delete return nil in case of success and true (conflict) or and exception else.
79
82
 
80
- require 'patron'
83
+ require 'net/http/persistent'
81
84
  require 'yajl'
82
85
  require 'rufus/jig'
83
86
 
@@ -108,9 +111,6 @@ put and delete return nil in case of success and true (conflict) or and exceptio
108
111
 
109
112
  c.detach('coffee0', '1-x-newrevision-whatever', 'picture')
110
113
 
111
- ...
112
-
113
- Warning : Patron, as of 0.4.5 is timing out when PUTting images.
114
114
 
115
115
  === on_change
116
116
 
@@ -155,6 +155,11 @@ To test the em-http-request HTTP transport, make sure the sinatra server is runn
155
155
  ruby test/test.rb -- --em
156
156
 
157
157
 
158
+ == known issues
159
+
160
+ - the EM based client cannot deal with non-ASCII paths
161
+
162
+
158
163
  == mailing list
159
164
 
160
165
  On the rufus-ruby list :
data/TODO.txt CHANGED
@@ -15,16 +15,18 @@
15
15
  [x] don't abuse exceptions ! (especially with couch)
16
16
  [o] couch.attach
17
17
  [o] couch.detach ?
18
+ [o] net http persistent
19
+ http://seattlerb.rubyforge.org/net-http-persistent/Net/HTTP/Persistent.html
20
+ [o] HttpResponse : DRY net and netp
21
+ [x] patron : http can steal each other's patron session (inside the same thread)
18
22
 
19
23
  [ ] HEAD
20
24
  [ ] redirections ? (Patron seem to understand them)
21
- [ ] patron : http can steal each other's patron session (inside the same thread)
22
25
  [ ] why not a :force_json ?
23
26
 
24
27
  [ ] make HttpCore detect do_request
25
28
 
26
29
  [ ] timeout per request ?
27
30
 
28
- [ ] net http persistent
29
- http://seattlerb.rubyforge.org/net-http-persistent/Net/HTTP/Persistent.html
31
+ [ ] netp : close() impl
30
32
 
@@ -48,9 +48,11 @@ end
48
48
 
49
49
  class Rufus::Jig::Http < Rufus::Jig::HttpCore
50
50
 
51
- def initialize ( host, port, opts={} )
51
+ require 'uri'
52
52
 
53
- super( host, port, opts )
53
+ def initialize( *args )
54
+
55
+ super( *args )
54
56
 
55
57
  @options[:user_agent] ||= "#{self.class} #{Rufus::Jig::VERSION} (em)"
56
58
  end
@@ -59,9 +61,14 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
59
61
  :em
60
62
  end
61
63
 
64
+ def close
65
+
66
+ # nothing to do
67
+ end
68
+
62
69
  protected
63
70
 
64
- def do_request ( method, path, data, opts )
71
+ def do_request( method, path, data, opts )
65
72
 
66
73
  args = {}
67
74
 
@@ -75,14 +82,19 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
75
82
  args[:timeout] = 5.0 # like Patron
76
83
  end
77
84
 
85
+ if auth = @options[:basic_auth]
86
+ args[:head].merge!( 'authorization' => auth )
87
+ end
88
+
78
89
  em_response( em_request( path ).send( method, args ) )
79
90
  end
80
91
 
81
92
  def em_request( uri = '/' )
93
+
82
94
  uri = Rufus::Jig.parse_uri( uri )
83
95
  uri = URI::HTTP.build(
84
- :host => ( uri.host || @host ),
85
- :port => ( uri.port || @port ),
96
+ :host => uri.host || @host,
97
+ :port => uri.port || @port,
86
98
  :path => uri.path,
87
99
  :query => uri.query
88
100
  )
@@ -91,6 +103,7 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
91
103
  end
92
104
 
93
105
  def em_response( em_client )
106
+
94
107
  th = Thread.current
95
108
 
96
109
  timedout = false
@@ -116,6 +129,7 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
116
129
  end
117
130
 
118
131
  def request_headers( options )
132
+
119
133
  headers = { 'user-agent' => @options[:user_agent] }
120
134
 
121
135
  %w[ Accept If-None-Match Content-Type ].each do |k|
@@ -22,39 +22,27 @@
22
22
  # Made in Japan.
23
23
  #++
24
24
 
25
- require 'thread'
26
25
  require 'net/http'
26
+ require 'rufus/jig/adapters/net_response'
27
27
 
28
28
 
29
- class Rufus::Jig::HttpResponse
30
-
31
- def initialize (nh_res)
32
-
33
- @original = nh_res
34
- @status = nh_res.code.to_i
35
- @body = nh_res.body
36
- @headers = {}
37
- nh_res.each { |k, v|
38
- @headers[k.split('-').collect { |s| s.capitalize }.join('-')] = v
39
- }
40
- end
41
- end
42
-
43
29
  class Rufus::Jig::Http < Rufus::Jig::HttpCore
44
30
 
45
- def initialize (host, port, opts={})
31
+ def initialize (*args)
46
32
 
47
- super(host, port, opts)
33
+ super(*args)
48
34
 
49
35
  @options[:user_agent] ||= "#{self.class} #{Rufus::Jig::VERSION} (net/http)"
50
-
51
- #@mutex = Mutex.new
52
36
  end
53
37
 
54
38
  def variant
55
39
  :net
56
40
  end
57
41
 
42
+ def close
43
+ # nothing to do
44
+ end
45
+
58
46
  protected
59
47
 
60
48
  def get_http (opts)
@@ -76,8 +64,6 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
76
64
 
77
65
  def do_request (method, path, data, opts)
78
66
 
79
- #@mutex.synchronize do
80
-
81
67
  path = '/' if path == ''
82
68
 
83
69
  req = eval("Net::HTTP::#{method.to_s.capitalize}").new(path)
@@ -85,14 +71,20 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
85
71
  req['User-Agent'] = @options[:user_agent]
86
72
  opts.each { |k, v| req[k] = v if k.is_a?(String) }
87
73
 
74
+ if auth = @options[:basic_auth]
75
+ req.basic_auth(*auth)
76
+ end
77
+
88
78
  req.body = data ? data : ''
89
79
 
90
80
  begin
91
- Rufus::Jig::HttpResponse.new(get_http(opts).start { |h| h.request(req) })
81
+ http = get_http(opts)
82
+ Rufus::Jig::HttpResponse.new(http.start { |h| h.request(req) })
92
83
  rescue Timeout::Error => te
93
84
  raise Rufus::Jig::TimeoutError
85
+ ensure
86
+ http.finish rescue nil
94
87
  end
95
- #end
96
88
  end
97
89
  end
98
90
 
@@ -0,0 +1,101 @@
1
+ #--
2
+ # Copyright (c) 2009-2010, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ require 'rufus/jig/adapters/net_response'
27
+
28
+
29
+ #
30
+ # Re-opening Net::HTTP::Persistent to add a shutdown_in_all_threads method.
31
+ #
32
+ class Net::HTTP::Persistent
33
+
34
+ # Shuts down this instance's connection in all the threads.
35
+ #
36
+ # (to avoid too many open files issues).
37
+ #
38
+ def shutdown_in_all_threads
39
+
40
+ Thread.list.each do |t|
41
+
42
+ if cons = t[@connection_key]
43
+ cons.each { |_, connection| connection.finish rescue IOError }
44
+ end
45
+
46
+ t[@connection_key] = nil
47
+ t[@request_key] = nil
48
+ end
49
+ end
50
+ end
51
+
52
+
53
+ class Rufus::Jig::Http < Rufus::Jig::HttpCore
54
+
55
+ def initialize (*args)
56
+
57
+ super(*args)
58
+
59
+ @options[:user_agent] ||=
60
+ "#{self.class} #{Rufus::Jig::VERSION} (net/http/persistent)"
61
+
62
+ @http = Net::HTTP::Persistent.new
63
+ end
64
+
65
+ def variant
66
+ :net_persistent
67
+ end
68
+
69
+ # Closes the connection
70
+ #
71
+ def close
72
+
73
+ #@http.shutdown
74
+ @http.shutdown_in_all_threads
75
+ end
76
+
77
+ protected
78
+
79
+ def do_request (method, path, data, opts)
80
+
81
+ path = '/' if path == ''
82
+
83
+ req = eval("Net::HTTP::#{method.to_s.capitalize}").new(path)
84
+
85
+ req['User-Agent'] = @options[:user_agent]
86
+ opts.each { |k, v| req[k] = v if k.is_a?(String) }
87
+
88
+ if auth = @options[:basic_auth]
89
+ req.basic_auth(*auth)
90
+ end
91
+
92
+ req.body = data ? data : ''
93
+
94
+ begin
95
+ Rufus::Jig::HttpResponse.new(@http.request(uri, req))
96
+ rescue Timeout::Error => te
97
+ raise Rufus::Jig::TimeoutError
98
+ end
99
+ end
100
+ end
101
+
@@ -0,0 +1,42 @@
1
+ #--
2
+ # Copyright (c) 2009-2010, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ #
27
+ # a Rufus::Jig wrapper for the server response.
28
+ #
29
+ class Rufus::Jig::HttpResponse
30
+
31
+ def initialize (nh_res)
32
+
33
+ @original = nh_res
34
+ @status = nh_res.code.to_i
35
+ @body = nh_res.body
36
+ @headers = {}
37
+ nh_res.each { |k, v|
38
+ @headers[k.split('-').collect { |s| s.capitalize }.join('-')] = v
39
+ }
40
+ end
41
+ end
42
+
@@ -36,23 +36,22 @@ end
36
36
 
37
37
  class Rufus::Jig::Http < Rufus::Jig::HttpCore
38
38
 
39
- def initialize (host, port, opts={})
39
+ def initialize (*args)
40
40
 
41
- super(host, port, opts)
42
- end
43
-
44
- def close
41
+ super(*args)
45
42
 
46
- # it's not really closing, it's rather making sure the patron
47
- # session can get collected as garbage
48
-
49
- #Thread.current[key] = nil
43
+ @options[:user_agent] ||= "#{self.class} #{Rufus::Jig::VERSION} (patron)"
50
44
  end
51
45
 
52
46
  def variant
53
47
  :patron
54
48
  end
55
49
 
50
+ def close
51
+
52
+ # nothing to do
53
+ end
54
+
56
55
  protected
57
56
 
58
57
  def get_patron (opts)
@@ -72,6 +71,12 @@ class Rufus::Jig::Http < Rufus::Jig::HttpCore
72
71
  @options[:user_agent] ||
73
72
  [ self.class, Rufus::Jig::VERSION, '(patron)' ].join(' ')
74
73
 
74
+ if auth = @options[:basic_auth]
75
+ patron.auth_type = :basic
76
+ patron.username = auth[0]
77
+ patron.password = auth[1]
78
+ end
79
+
75
80
  patron
76
81
  end
77
82
 
@@ -22,6 +22,7 @@
22
22
  # Made in Japan.
23
23
  #++
24
24
 
25
+ require 'base64'
25
26
  require 'socket'
26
27
  # for #on_change
27
28
 
@@ -39,9 +40,9 @@ module Rufus::Jig
39
40
 
40
41
  def initialize (*args)
41
42
 
42
- @http, @path, payload, @opts = Rufus::Jig::Http.extract_http(false, *args)
43
+ @http = Rufus::Jig::Http.new(*args)
43
44
 
44
- @path ||= '/'
45
+ @path = @http._path || '/'
45
46
  end
46
47
 
47
48
  def name
@@ -248,15 +249,43 @@ module Rufus::Jig
248
249
  query = {
249
250
  'feed' => 'continuous',
250
251
  'heartbeat' => opts[:heartbeat] || 20_000 }
252
+ #'since' => 0 } # that's already the default
251
253
  query['include_docs'] = true if block.arity > 2
252
254
  query = query.map { |k, v| "#{k}=#{v}" }.join('&')
253
255
 
254
256
  socket = TCPSocket.open(@http.host, @http.port)
255
257
 
258
+ auth = @http.options[:basic_auth]
259
+
260
+ if auth
261
+ auth = Base64.encode64(auth.join(':')).strip
262
+ auth = "Authorization: Basic #{auth}\r\n"
263
+ else
264
+ auth = ''
265
+ end
266
+
256
267
  socket.print("GET /#{path}/_changes?#{query} HTTP/1.1\r\n")
257
268
  socket.print("User-Agent: rufus-jig #{Rufus::Jig::VERSION}\r\n")
269
+ #socket.print("Accept: application/json;charset=UTF-8\r\n")
270
+ socket.print(auth)
258
271
  socket.print("\r\n")
259
272
 
273
+ # consider reply
274
+
275
+ answer = socket.gets.strip
276
+ status = answer.match(/^HTTP\/.+ (\d{3}) /)[1].to_i
277
+
278
+ raise Rufus::Jig::HttpError.new(status, answer) if status != 200
279
+
280
+ # discard headers
281
+
282
+ loop do
283
+ data = socket.gets
284
+ break if data.nil? || data == "\r\n"
285
+ end
286
+
287
+ # the on_change loop
288
+
260
289
  loop do
261
290
  data = socket.gets
262
291
  break if data.nil?