dagger 1.6.1 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d9231125c099e4e5e391e00719bede1da167f044242b6c2604d9690dee0d842
4
- data.tar.gz: e997c56d4dc24be6cb747e5f1c350cffe2e804dd852a712ca3979381e4c6df03
3
+ metadata.gz: 692b25152c7fd3229ad3dc13bd25250b613e55a211e489b8835258b18405db19
4
+ data.tar.gz: 5560f64e580e4f72f9e243eaf1c5eed1b389e439074607dc24bdd8fbe9c532f0
5
5
  SHA512:
6
- metadata.gz: 3e8c581c55ac7eb75af99f1ec50e2d85324d30f45faae37b45bf920f0bc6a63b5c38ba2ae99f5b98913698208f158dc8f0775e9b2833958a92a4a88dce392547
7
- data.tar.gz: f870dc8b936d2e64e4d4dc0b06a94cd22527c0675f3317b5cb69b1c6c6e6263fd5a58a6fe35e37e8b87e2f74b535713127c92c525afbdcdb9e56ca9e64def6b8
6
+ metadata.gz: 85d160f0130c12fde8b2723cd54bcaf4a75c4f48319cb040c4725a93e6cc09d4f8a68a7977135f33c38922583cc8de481bc078ea6d3ff5636c42c3154a5dca74
7
+ data.tar.gz: 9bd29716a9e84b75e898f71b00e8fc145550fcccd15b2c0006172e56e3e5454b62668bc4536dacde371a33b429f437a942354368de4cce0394ff73296cefa8e8
@@ -19,7 +19,6 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency "rspec-mocks"
20
20
  s.add_development_dependency "rspec-expectations"
21
21
 
22
- s.add_runtime_dependency "net-http-persistent", "~> 3.0"
23
22
  s.add_runtime_dependency "oj", "~> 2.1"
24
23
  s.add_runtime_dependency "ox", "~> 2.4"
25
24
 
@@ -1,10 +1,10 @@
1
1
  require 'dagger/version'
2
2
  require 'dagger/response'
3
3
  require 'dagger/parsers'
4
-
5
- require 'net/http/persistent'
4
+ require 'dagger/connection_manager'
6
5
  require 'net/https'
7
6
  require 'base64'
7
+ require 'erb'
8
8
 
9
9
  class URI::HTTP
10
10
  def scheme_and_host
@@ -14,13 +14,19 @@ end
14
14
 
15
15
  module Dagger
16
16
 
17
- DAGGER_NAME = "Dagger/#{VERSION}"
17
+ DAGGER_NAME = "Dagger/#{VERSION}".freeze
18
18
  REDIRECT_CODES = [301, 302, 303].freeze
19
19
  DEFAULT_RETRY_WAIT = 5.freeze # seconds
20
20
  DEFAULT_HEADERS = {
21
21
  'Accept' => '*/*',
22
- 'User-Agent' => "#{DAGGER_NAME} (Ruby Net::HTTP Wrapper, like curl)"
23
- }
22
+ 'User-Agent' => "#{DAGGER_NAME} (Ruby Net::HTTP wrapper, like curl)"
23
+ }.freeze
24
+
25
+ DEFAULTS = {
26
+ open_timeout: 10,
27
+ read_timeout: 10,
28
+ keep_alive_timeout: 10
29
+ }.freeze
24
30
 
25
31
  module Utils
26
32
 
@@ -34,7 +40,7 @@ module Dagger
34
40
  end
35
41
 
36
42
  def self.resolve_uri(uri, host = nil, query = nil)
37
- uri = host + uri if uri.to_s['//'].nil? && host
43
+ uri = host + uri if uri[0] == '/' && host
38
44
  uri = parse_uri(uri.to_s)
39
45
  uri.path.sub!(/\?.*|$/, '?' + Utils.encode(query)) if query and query.any?
40
46
  uri
@@ -50,7 +56,7 @@ module Dagger
50
56
  when Array then obj.map { |v| encode(v, "#{key}[]") }.join('&')
51
57
  when nil then ''
52
58
  else
53
- "#{key}=#{URI.escape(obj.to_s)}"
59
+ "#{key}=#{ERB::Util.url_encode(obj.to_s)}"
54
60
  end
55
61
  end
56
62
 
@@ -62,25 +68,40 @@ module Dagger
62
68
 
63
69
  class Client
64
70
 
65
- def self.init(uri, opts)
66
- uri = Utils.parse_uri(uri)
67
- http = if opts.delete(:persistent)
68
- pool_size = opts[:pool_size] || Net::HTTP::Persistent::DEFAULT_POOL_SIZE
69
- Net::HTTP::Persistent.new(name: DAGGER_NAME, pool_size: pool_size)
70
- else
71
- Net::HTTP.new(opts[:ip] || uri.host, uri.port)
71
+ def self.init_persistent(opts = {})
72
+ # this line below forces one connection manager between multiple threads
73
+ # @persistent ||= Dagger::ConnectionManager.new(opts)
74
+
75
+ # here we initialize a connection manager for each thread
76
+ Thread.current[:dagger_persistent] ||= begin
77
+ Dagger::ConnectionManager.new(opts)
72
78
  end
79
+ end
80
+
81
+ def self.init_connection(uri, opts = {})
82
+ http = Net::HTTP.new(opts[:ip] || uri.host, uri.port)
73
83
 
74
84
  if uri.port == 443
75
85
  http.use_ssl = true if http.respond_to?(:use_ssl=) # persistent does it automatically
76
86
  http.verify_mode = opts[:verify_ssl] === false ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
77
87
  end
78
88
 
79
- [:open_timeout, :read_timeout, :ssl_version, :ciphers].each do |key|
80
- http.send("#{key}=", opts[key]) if opts.has_key?(key)
89
+ [:keep_alive_timeout, :open_timeout, :read_timeout, :ssl_version, :ciphers].each do |key|
90
+ http.send("#{key}=", opts[key] || DEFAULTS[key]) if (opts.has_key?(key) || DEFAULTS.has_key?(key))
91
+ end
92
+
93
+ http
94
+ end
95
+
96
+ def self.init(uri, opts)
97
+ uri = Utils.parse_uri(uri)
98
+
99
+ http = if opts.delete(:persistent)
100
+ init_persistent(opts)
101
+ else
102
+ init_connection(uri, opts)
81
103
  end
82
104
 
83
- # new(http, [uri.scheme, uri.host].join('://'))
84
105
  new(http, uri.scheme_and_host)
85
106
  end
86
107
 
@@ -91,7 +112,9 @@ module Dagger
91
112
  def get(uri, opts = {})
92
113
  uri = Utils.resolve_uri(uri, @host, opts[:query])
93
114
 
94
- raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}") if @host != uri.scheme_and_host
115
+ if @host != uri.scheme_and_host
116
+ raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}")
117
+ end
95
118
 
96
119
  opts[:follow] = 10 if opts[:follow] == true
97
120
  headers = opts[:headers] || {}
@@ -109,23 +132,23 @@ module Dagger
109
132
  @http.start unless @http.started?
110
133
  resp, data = @http.request(request)
111
134
  else # persistent
112
- puts @http.pool.inspect
113
- resp, data = @http.request(uri, request)
135
+ resp, data = @http.send_request(uri, request)
114
136
  end
115
137
 
116
138
  if REDIRECT_CODES.include?(resp.code.to_i) && resp['Location'] && (opts[:follow] && opts[:follow] > 0)
117
139
  opts[:follow] -= 1
118
- puts "Following redirect to #{resp['Location']}"
140
+ debug "Following redirect to #{resp['Location']}"
119
141
  return get(resp['Location'], opts)
120
142
  end
121
143
 
122
144
  @response = build_response(resp, data || resp.body)
123
145
 
124
146
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL, Timeout::Error, \
125
- SocketError, EOFError, Net::ReadTimeout, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
147
+ Net::OpenTimeout, Net::ReadTimeout, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, \
148
+ SocketError, EOFError, OpenSSL::SSL::SSLError => e
126
149
 
127
150
  if retries = opts[:retries] and retries.to_i > 0
128
- puts "Got #{e.class}! Retrying in a sec (#{retries} retries left)"
151
+ debug "Got #{e.class}! Retrying in a sec (#{retries} retries left)"
129
152
  sleep (opts[:retry_wait] || DEFAULT_RETRY_WAIT)
130
153
  get(uri, opts.merge(retries: retries - 1))
131
154
  else
@@ -156,7 +179,10 @@ module Dagger
156
179
  end
157
180
 
158
181
  uri = Utils.resolve_uri(uri, @host)
159
- raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}") if @host != uri.scheme_and_host
182
+ if @host != uri.scheme_and_host
183
+ raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}")
184
+ end
185
+
160
186
  headers = DEFAULT_HEADERS.merge(opts[:headers] || {})
161
187
 
162
188
  query = if data.is_a?(String)
@@ -184,16 +210,17 @@ module Dagger
184
210
  req = Kernel.const_get("Net::HTTP::#{method.capitalize}").new(uri.path, headers)
185
211
  # req.set_form_data(query)
186
212
  req.body = query
187
- resp, data = @http.request(uri, req)
213
+ resp, data = @http.send_request(uri, req)
188
214
  end
189
215
 
190
216
  @response = build_response(resp, data || resp.body)
191
217
 
192
218
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL, Timeout::Error, \
193
- SocketError, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
219
+ Net::OpenTimeout, Net::ReadTimeout, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, \
220
+ SocketError, EOFError, OpenSSL::SSL::SSLError => e
194
221
 
195
222
  if method.to_s.downcase != 'get' && retries = opts[:retries] and retries.to_i > 0
196
- puts "[#{DAGGER_NAME}] Got #{e.class}! Retrying in a sec (#{retries} retries left)"
223
+ debug "[#{DAGGER_NAME}] Got #{e.class}! Retrying in a sec (#{retries} retries left)"
197
224
  sleep (opts[:retry_wait] || DEFAULT_RETRY_WAIT)
198
225
  request(method, uri, data, opts.merge(retries: retries - 1))
199
226
  else
@@ -206,7 +233,7 @@ module Dagger
206
233
  end
207
234
 
208
235
  def open(&block)
209
- if @http.is_a?(Net::HTTP::Persistent)
236
+ if @http.is_a?(Dagger::ConnectionManager)
210
237
  instance_eval(&block)
211
238
  else
212
239
  @http.start do
@@ -216,7 +243,7 @@ module Dagger
216
243
  end
217
244
 
218
245
  def close
219
- if @http.is_a?(Net::HTTP::Persistent)
246
+ if @http.is_a?(Dagger::ConnectionManager)
220
247
  @http.shutdown # calls finish on pool connections
221
248
  else
222
249
  @http.finish if @http.started?
@@ -225,6 +252,10 @@ module Dagger
225
252
 
226
253
  private
227
254
 
255
+ def debug(str)
256
+ puts str if ENV['DEBUGGING']
257
+ end
258
+
228
259
  def build_response(resp, body)
229
260
  resp.extend(Response)
230
261
  resp.set_body(body) unless resp.body
@@ -270,4 +301,4 @@ module Dagger
270
301
 
271
302
  end
272
303
 
273
- end
304
+ end
@@ -0,0 +1,59 @@
1
+ module Dagger
2
+
3
+ class ConnectionManager
4
+
5
+ def initialize(opts = {})
6
+ @opts = {}
7
+ @active_connections = {}
8
+ @mutex = Mutex.new
9
+ end
10
+
11
+ def shutdown
12
+ @mutex.synchronize do
13
+ # puts "Shutting down connections: #{@active_connections.count}"
14
+ @active_connections.each do |_, connection|
15
+ connection.finish
16
+ end
17
+ @active_connections = {}
18
+ end
19
+ end
20
+
21
+ # Gets a connection for a given URI. This is for internal use only as it's
22
+ # subject to change (we've moved between HTTP client schemes in the past
23
+ # and may do it again).
24
+ #
25
+ # `uri` is expected to be a string.
26
+ def connection_for(uri)
27
+ @mutex.synchronize do
28
+ connection = @active_connections[[uri.host, uri.port]]
29
+
30
+ if connection.nil?
31
+ connection = Dagger::Client.init_connection(uri, @opts)
32
+ connection.start
33
+
34
+ @active_connections[[uri.host, uri.port]] = connection
35
+ # puts "#{@active_connections.count} connections"
36
+ end
37
+
38
+ connection
39
+ end
40
+ end
41
+
42
+ # Executes an HTTP request to the given URI with the given method. Also
43
+ # allows a request body, headers, and query string to be specified.
44
+ def send_request(uri, request)
45
+ connection = connection_for(uri)
46
+ @mutex.synchronize do
47
+ begin
48
+ connection.request(request)
49
+ rescue StandardError => err
50
+ err
51
+ end
52
+ end.tap do |result|
53
+ raise(result) if result.is_a?(StandardError)
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -5,12 +5,20 @@ XMLNode = Struct.new(:name, :text, :attributes, :children) do
5
5
  alias_method :to_s, :text
6
6
  alias_method :value, :text
7
7
 
8
+ def to_node
9
+ self
10
+ end
11
+
8
12
  def count
9
13
  raise "Please call #children.count"
10
14
  end
11
15
 
12
- def to_hash
13
- self # for backwards compat
16
+ def keys
17
+ @keys ||= children.collect(&:name)
18
+ end
19
+
20
+ def is_array?
21
+ keys.count != keys.uniq.count
14
22
  end
15
23
 
16
24
  # this lets us traverse an parsed object like this:
@@ -20,30 +28,33 @@ XMLNode = Struct.new(:name, :text, :attributes, :children) do
20
28
  found.empty? ? nil : found.size == 1 ? found.first : found
21
29
  end
22
30
 
31
+ # returns list of XMLNodes with matching names
23
32
  def slice(*arr)
24
33
  Array(arr).flatten.map { |key| self[key] }
25
34
  end
26
35
 
27
- def values(arr = nil, include_empty: false)
28
- if arr
29
- Array(arr).flatten.each_with_object({}) do |key, memo|
36
+ def values(keys_arr = nil, include_empty: false)
37
+ if keys_arr
38
+ Array(keys_arr).flatten.each_with_object({}) do |key, memo|
30
39
  if found = self[key] and (found.to_s || include_empty)
31
40
  memo[key] = found.to_s
32
41
  end
33
42
  end
43
+ elsif is_array?
44
+ children.map(&:values)
34
45
  else
35
- children.each_with_object({}) do |child, memo|
36
- if child.to_s || include_empty
37
- memo[child.name] = child.to_s
38
- end
46
+ children.each_with_object({}) do |child, memo|
47
+ memo[child.name] = child.children.any? ? child.values : child.text
39
48
  end
40
49
  end
41
50
  end
42
51
 
52
+ alias_method :to_hash, :values
53
+ alias_method :to_h, :values
54
+
43
55
  def dig(*paths)
44
56
  list = Array(paths).flatten
45
57
  res = list.reduce([self]) do |parents, key|
46
-
47
58
  if parents
48
59
  found = parents.map do |parent|
49
60
  parent.children.select { |node| node.name.to_s == key.to_s }
@@ -79,14 +90,14 @@ XMLNode = Struct.new(:name, :text, :attributes, :children) do
79
90
  end
80
91
 
81
92
  class Ox::Document
82
- def to_hash
83
- nodes.first.to_hash
93
+ def to_node
94
+ nodes.first.to_node
84
95
  end
85
96
  end
86
97
 
87
98
  class Ox::Element
88
- def to_hash
89
- children = nodes.map { |n| n.class == self.class ? n.to_hash : nil }.compact
99
+ def to_node
100
+ children = nodes.map { |n| n.class == self.class ? n.to_node : nil }.compact
90
101
  XMLNode.new(value, text, attributes, children)
91
102
  end
92
103
  end
@@ -26,7 +26,7 @@ class Parsers
26
26
 
27
27
  def text_xml(body)
28
28
  if res = Ox.parse(body)
29
- res.to_hash
29
+ res.to_node
30
30
  end
31
31
  rescue Ox::ParseError
32
32
  nil
@@ -1,7 +1,7 @@
1
1
  module Dagger
2
2
  MAJOR = 1
3
- MINOR = 6
4
- PATCH = 1
3
+ MINOR = 9
4
+ PATCH = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, PATCH].join('.')
7
7
  end
@@ -0,0 +1,151 @@
1
+ require_relative '../dagger'
2
+
3
+ module Dagger
4
+
5
+ module Wrapper
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ def base_url(str = nil)
13
+ if str # set
14
+ @base_url = str
15
+ else
16
+ @base_url or raise "base_url unset!" # get
17
+ end
18
+ end
19
+
20
+ def base_options(opts = nil)
21
+ if opts # set
22
+ @base_options = opts
23
+ else
24
+ @base_options or raise "base_url unset!" # get
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize(opts = {})
30
+ @logger = opts.delete(:logger)
31
+ @options = opts
32
+ end
33
+
34
+ def get(path, params = {}, opts = {})
35
+ request(:get, path, params, opts)
36
+ end
37
+
38
+ def post(path, params = {}, opts = {})
39
+ request(:post, path, params, opts)
40
+ end
41
+
42
+ def put(path, params = {}, opts = {})
43
+ request(:put, path, params, opts)
44
+ end
45
+
46
+ def patch(path, params = {}, opts = {})
47
+ request(:patch, path, params, opts)
48
+ end
49
+
50
+ def delete(path, params = {}, opts = {})
51
+ request(:delete, path, params, opts)
52
+ end
53
+
54
+ def request(method, path, params = {}, opts = nil)
55
+ url = self.class.base_url + path
56
+ resp = benchmark("#{method} #{path}") do
57
+ http.request(method, url, params, base_options.merge(opts))
58
+ end
59
+
60
+ handle_response(resp, method, path, params)
61
+ end
62
+
63
+ def connect(&block)
64
+ open_http
65
+ if block_given?
66
+ yield
67
+ close_http
68
+ else
69
+ at_exit { close_http }
70
+ end
71
+ end
72
+
73
+ def disconnect
74
+ close_http
75
+ end
76
+
77
+ private
78
+ attr_reader :options
79
+
80
+ def handle_response(resp, method, path, params)
81
+ resp
82
+ end
83
+
84
+ def base_options
85
+ {}
86
+ end
87
+
88
+ def request_options
89
+ self.class.base_options.merge(base_options)
90
+ end
91
+
92
+ def benchmark(message, &block)
93
+ log(message)
94
+ start = Time.now
95
+ resp = yield
96
+ time = Time.now - start
97
+ log("Got response in #{time.round(2)} secs")
98
+ resp
99
+ end
100
+
101
+ def log(str)
102
+ logger.info(str)
103
+ end
104
+
105
+ def logger
106
+ @logger ||= begin
107
+ require 'logger'
108
+ Logger.new(@options[:logfile])
109
+ end
110
+ end
111
+
112
+ def http
113
+ @http || Dagger
114
+ end
115
+
116
+ def open_http
117
+ raise "Already open!" if @http
118
+ @http = Dagger.open(self.class.base_url)
119
+ end
120
+
121
+ def close_http
122
+ @http.close if @http
123
+ @http = nil
124
+ end
125
+
126
+ # def wrap(hash)
127
+ # Entity.new(hash)
128
+ # end
129
+
130
+ # class Entity
131
+ # def initialize(props)
132
+ # @props = props
133
+ # end
134
+
135
+ # def get(prop)
136
+ # val = @props[name.to_s]
137
+ # end
138
+
139
+ # def method_missing(name, args, &block)
140
+ # if @props.key?(name.to_s)
141
+ # get(name)
142
+ # else
143
+ # # raise NoMethodError, "undefined method #{name}"
144
+ # super
145
+ # end
146
+ # end
147
+ # end
148
+
149
+ end
150
+
151
+ end
@@ -23,7 +23,7 @@ describe 'arguments' do
23
23
  describe 'invalid URL' do
24
24
 
25
25
  it 'raises error' do
26
- expect { send_request('asd123.rewqw') }.to raise_error(ArgumentError)
26
+ expect { send_request('asd123.rewqw') }.to raise_error(SocketError)
27
27
  end
28
28
 
29
29
  end
@@ -36,6 +36,15 @@ describe 'arguments' do
36
36
 
37
37
  end
38
38
 
39
+ describe 'host without protocol' do
40
+
41
+ it 'works' do
42
+ expect(send_request('www.google.com')).to be_a(Net::HTTPResponse)
43
+ end
44
+
45
+ end
46
+
47
+
39
48
  describe 'valid host' do
40
49
 
41
50
  it 'works' do
@@ -14,6 +14,9 @@ describe 'Parsers' do
14
14
 
15
15
  before do
16
16
  allow(Net::HTTP).to receive(:new).and_return(fake_http)
17
+ allow(fake_http).to receive(:keep_alive_timeout=).and_return(true)
18
+ allow(fake_http).to receive(:read_timeout=).and_return(true)
19
+ allow(fake_http).to receive(:open_timeout=).and_return(true)
17
20
  allow(fake_http).to receive(:verify_mode=).and_return(true)
18
21
  allow(fake_http).to receive(:post).and_return(fake_resp)
19
22
  end
@@ -100,7 +103,7 @@ describe 'Parsers' do
100
103
  it 'returns XMLNode obj' do
101
104
  res = send_request.data
102
105
  expect(res).to be_a(XMLNode)
103
- expect(res.to_hash).to eql(res)
106
+ expect(res.to_node).to eql(res)
104
107
  expect(res['foo']).to be_a(XMLNode)
105
108
  expect(res['foo'].text).to eql('123')
106
109
 
@@ -119,16 +122,30 @@ describe 'Parsers' do
119
122
  <nested>
120
123
  <item>
121
124
  <title attr="downcased">foobar</title>
125
+ <category>cat</category>
126
+ <published>true</published>
122
127
  </item>
123
128
  </nested>
124
129
  </xml>
125
130
  )
126
131
 
127
- it 'works' do
128
- doc = Ox.parse(xml)
129
- obj = doc.to_hash
132
+ describe '#to_node' do
133
+ it 'works as expected' do
134
+ doc = Ox.parse(xml)
135
+ obj = doc.to_node
136
+ expect(obj[:nested][:item][:title].text).to eql('foobar')
137
+ end
138
+ end
130
139
 
131
- expect(obj[:nested][:item][:title].text).to eql('foobar')
140
+ describe '#values' do
141
+ it 'works as expected' do
142
+ doc = Ox.parse(xml)
143
+ obj = doc.to_node.values
144
+ expect(obj).to eql({
145
+ "foo" => "test",
146
+ "nested" => {"item"=>{"category"=>"cat", "published"=>"true", "title"=>"foobar"}},
147
+ })
148
+ end
132
149
  end
133
150
 
134
151
  end
@@ -17,4 +17,50 @@ describe 'Persistent mode' do
17
17
  end
18
18
  end
19
19
 
20
+ end
21
+
22
+ describe 'using threads' do
23
+
24
+ def get(url)
25
+ @http.get(url)
26
+ end
27
+
28
+ def connect(host)
29
+ raise if @http
30
+ @http = Dagger.open(host)
31
+ end
32
+
33
+ def disconnect
34
+ raise if @http.nil?
35
+ @http.close
36
+ @http = nil
37
+ end
38
+
39
+ it 'works' do
40
+ thread_count = 10
41
+ urls_count = 100
42
+ host = 'https://postman-echo.com'
43
+ urls = urls_count.times.map { |i| "/get?page/#{i}" }
44
+ result = []
45
+
46
+ mutex = Mutex.new
47
+ thread_count.times.map do
48
+ Thread.new(urls, result) do |urls, result|
49
+ # mutex.synchronize { Dagger.open(host) }
50
+ http = Dagger.open(host)
51
+ while url = mutex.synchronize { urls.pop }
52
+ puts "Fetching #{url}"
53
+ resp = http.get(url)
54
+ mutex.synchronize do
55
+ result.push(resp.code)
56
+ end
57
+ end
58
+ # mutex.synchronize { http.close }
59
+ http.close
60
+ end
61
+ end.each(&:join)
62
+
63
+ expect(result.count).to eq(urls_count)
64
+ end
65
+
20
66
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dagger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomás Pollak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-31 00:00:00.000000000 Z
11
+ date: 2020-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: net-http-persistent
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '3.0'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '3.0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: oj
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -124,10 +110,12 @@ files:
124
110
  - bin/dagger
125
111
  - dagger.gemspec
126
112
  - lib/dagger.rb
113
+ - lib/dagger/connection_manager.rb
127
114
  - lib/dagger/ox_extension.rb
128
115
  - lib/dagger/parsers.rb
129
116
  - lib/dagger/response.rb
130
117
  - lib/dagger/version.rb
118
+ - lib/dagger/wrapper.rb
131
119
  - spec/arguments_spec.rb
132
120
  - spec/ip_connect_spec.rb
133
121
  - spec/parsers_spec.rb