rufus-jig 0.1.21 → 0.1.22

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