curb_threadpool 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +12 -0
- data/LICENSE +23 -0
- data/README.md +41 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/curb_threadpool.gemspec +69 -0
- data/lib/curb_threadpool.rb +104 -0
- data/test/helper.rb +18 -0
- data/test/test_curb_threadpool.rb +7 -0
- metadata +192 -0
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
group :development do
|
4
|
+
gem "yard"
|
5
|
+
gem "redcarpet"
|
6
|
+
gem "shoulda", ">= 0"
|
7
|
+
gem "rdoc", "~> 3.12"
|
8
|
+
gem "bundler", "~> 1.0.0"
|
9
|
+
gem "jeweler", "~> 1.8.3"
|
10
|
+
gem "rcov", ">= 0", :platforms => :mri_18
|
11
|
+
gem "simplecov", :platforms => :mri_19
|
12
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2012, Chetan Sarva
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
Redistributions in binary form must reproduce the above copyright notice, this
|
11
|
+
list of conditions and the following disclaimer in the documentation and/or
|
12
|
+
other materials provided with the distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
15
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
16
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
17
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
18
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
19
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
20
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
21
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
22
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
23
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# curb_threadpool
|
2
|
+
|
3
|
+
A multi-threaded worker pool for Curb.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
ct = Curl::ThreadPool.new
|
8
|
+
reqs = []
|
9
|
+
10000.times { |i| ct[i] = "http://localhost/test" }
|
10
|
+
responses = ct.exec() # => contains keyed response hash
|
11
|
+
|
12
|
+
or
|
13
|
+
|
14
|
+
ct = Curl::ThreadPool.new
|
15
|
+
reqs = []
|
16
|
+
10000.times { reqs << "http://localhost/test" }
|
17
|
+
ct.get(reqs) # => contains responses in same order as reqs
|
18
|
+
|
19
|
+
In order to re-use connections opened by the pool, hang on to it:
|
20
|
+
|
21
|
+
Thread.current[:my_pool] ||= Curl::ThreadPool.new
|
22
|
+
Thread.current[:my_pool].get("http://localhost/test")
|
23
|
+
|
24
|
+
This way, if you frequently make requests to the same host (e.g., an API
|
25
|
+
service) then the connections will be kept open as long as possible and
|
26
|
+
re-used, speeding up your response time. Curl will automatically
|
27
|
+
reconnected when necessary.
|
28
|
+
|
29
|
+
## Contributing to curb_threadpool
|
30
|
+
|
31
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
32
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
33
|
+
* Fork the project.
|
34
|
+
* Start a feature/bugfix branch.
|
35
|
+
* Commit and push until you are happy with your contribution.
|
36
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
37
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
38
|
+
|
39
|
+
## Copyright
|
40
|
+
|
41
|
+
Copyright (c) 2012 Chetan Sarva. See LICENSE for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "curb_threadpool"
|
18
|
+
gem.homepage = "http://github.com/chetan/curb_threadpool"
|
19
|
+
gem.license = "BSD"
|
20
|
+
gem.summary = %Q{A multi-threaded worker pool for Curb}
|
21
|
+
gem.description = %Q{A multi-threaded worker pool for Curb}
|
22
|
+
gem.email = "chetan@pixelcop.net"
|
23
|
+
gem.authors = ["Chetan Sarva"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
if Object.const_defined? :Rcov
|
36
|
+
require 'rcov/rcovtask'
|
37
|
+
Rcov::RcovTask.new do |test|
|
38
|
+
test.libs << 'test'
|
39
|
+
test.pattern = 'test/**/test_*.rb'
|
40
|
+
test.verbose = true
|
41
|
+
test.rcov_opts << '--exclude "gems/*"'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default => :test
|
46
|
+
|
47
|
+
require 'yard'
|
48
|
+
YARD::Rake::YardocTask.new do |t|
|
49
|
+
t.options = [ "--files", "README.md,LICENSE" ]
|
50
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{curb_threadpool}
|
8
|
+
s.version = "0.5.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Chetan Sarva"]
|
12
|
+
s.date = %q{2012-05-08}
|
13
|
+
s.description = %q{A multi-threaded worker pool for Curb}
|
14
|
+
s.email = %q{chetan@pixelcop.net}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"Gemfile",
|
21
|
+
"LICENSE",
|
22
|
+
"README.md",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"curb_threadpool.gemspec",
|
26
|
+
"lib/curb_threadpool.rb",
|
27
|
+
"test/helper.rb",
|
28
|
+
"test/test_curb_threadpool.rb"
|
29
|
+
]
|
30
|
+
s.homepage = %q{http://github.com/chetan/curb_threadpool}
|
31
|
+
s.licenses = ["BSD"]
|
32
|
+
s.require_paths = ["lib"]
|
33
|
+
s.rubygems_version = %q{1.5.0}
|
34
|
+
s.summary = %q{A multi-threaded worker pool for Curb}
|
35
|
+
|
36
|
+
if s.respond_to? :specification_version then
|
37
|
+
s.specification_version = 3
|
38
|
+
|
39
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
40
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
41
|
+
s.add_development_dependency(%q<redcarpet>, [">= 0"])
|
42
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
43
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
44
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
45
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
46
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
47
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
48
|
+
else
|
49
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
50
|
+
s.add_dependency(%q<redcarpet>, [">= 0"])
|
51
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
52
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
53
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
54
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
55
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
56
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
57
|
+
end
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
60
|
+
s.add_dependency(%q<redcarpet>, [">= 0"])
|
61
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
62
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
63
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
65
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
66
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
@@ -0,0 +1,104 @@
|
|
1
|
+
|
2
|
+
# A multi-threaded worker pool for Curb
|
3
|
+
class Curl::ThreadPool
|
4
|
+
|
5
|
+
attr_reader :results
|
6
|
+
|
7
|
+
def initialize(size=4)
|
8
|
+
@size = size
|
9
|
+
reset()
|
10
|
+
end
|
11
|
+
|
12
|
+
# Add a URL to be fetched
|
13
|
+
#
|
14
|
+
# @param [Object] key to use for request
|
15
|
+
# @param [String] url URL to fetch
|
16
|
+
def []=(key, url)
|
17
|
+
@reqs[key] = url
|
18
|
+
end
|
19
|
+
alias :add :[]=
|
20
|
+
|
21
|
+
# Wait for all threads to complete
|
22
|
+
def join
|
23
|
+
@threads.each { |t| t.join }
|
24
|
+
@threads.clear
|
25
|
+
end
|
26
|
+
|
27
|
+
# Close all active Curl connections
|
28
|
+
def close
|
29
|
+
@clients.each { |c| c.reset(); c.close() } if @clients
|
30
|
+
end
|
31
|
+
|
32
|
+
# Reset the ThreadPool
|
33
|
+
def reset
|
34
|
+
close()
|
35
|
+
@reqs = {}
|
36
|
+
@results = {}
|
37
|
+
@clients = []
|
38
|
+
@threads = []
|
39
|
+
@size.times{ @clients << Curl::Easy.new }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Utility method for retrieving a list of URLs
|
43
|
+
#
|
44
|
+
# @param [Array<String>] urls list of URLs
|
45
|
+
# @return [Array] array of response bodies
|
46
|
+
def get(urls)
|
47
|
+
if urls.nil? or urls.empty? then
|
48
|
+
return {}
|
49
|
+
end
|
50
|
+
|
51
|
+
urls = [urls] if not urls.kind_of? Array
|
52
|
+
urls.each_with_index do |url, i|
|
53
|
+
@reqs[i] = url.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
results = exec()
|
57
|
+
|
58
|
+
ret = []
|
59
|
+
(0..results.size-1).each do |i|
|
60
|
+
ret << results[i]
|
61
|
+
end
|
62
|
+
|
63
|
+
return ret
|
64
|
+
end
|
65
|
+
|
66
|
+
# Execute requests. By default, will block until complete and return results.
|
67
|
+
#
|
68
|
+
# @param [Boolean] no_block If true, will not wait for requests to finish.
|
69
|
+
# (Default=false)
|
70
|
+
#
|
71
|
+
# @param [Block] block If passed, responses will be passed into the callback
|
72
|
+
# instead of being returned directly
|
73
|
+
#
|
74
|
+
# @return [Hash<Key, String>] Hash of responses, if no block given
|
75
|
+
def exec(no_block=false, &block)
|
76
|
+
@clients.each do |client|
|
77
|
+
@threads << Thread.new do
|
78
|
+
|
79
|
+
loop do
|
80
|
+
break if @reqs.empty?
|
81
|
+
(key, url) = @reqs.shift
|
82
|
+
client.url = url
|
83
|
+
client.http_get
|
84
|
+
if block then
|
85
|
+
yield(key, client.body_str)
|
86
|
+
else
|
87
|
+
@results[key] = client.body_str
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
return {} if no_block
|
95
|
+
|
96
|
+
join()
|
97
|
+
return true if block
|
98
|
+
|
99
|
+
ret = @results
|
100
|
+
@results = {}
|
101
|
+
return ret
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'curb_threadpool'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: curb_threadpool
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 11
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Chetan Sarva
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-05-08 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: yard
|
23
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
prerelease: false
|
33
|
+
type: :development
|
34
|
+
requirement: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: redcarpet
|
37
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
prerelease: false
|
47
|
+
type: :development
|
48
|
+
requirement: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: shoulda
|
51
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
prerelease: false
|
61
|
+
type: :development
|
62
|
+
requirement: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rdoc
|
65
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ~>
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 31
|
71
|
+
segments:
|
72
|
+
- 3
|
73
|
+
- 12
|
74
|
+
version: "3.12"
|
75
|
+
prerelease: false
|
76
|
+
type: :development
|
77
|
+
requirement: *id004
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 23
|
86
|
+
segments:
|
87
|
+
- 1
|
88
|
+
- 0
|
89
|
+
- 0
|
90
|
+
version: 1.0.0
|
91
|
+
prerelease: false
|
92
|
+
type: :development
|
93
|
+
requirement: *id005
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: jeweler
|
96
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
hash: 49
|
102
|
+
segments:
|
103
|
+
- 1
|
104
|
+
- 8
|
105
|
+
- 3
|
106
|
+
version: 1.8.3
|
107
|
+
prerelease: false
|
108
|
+
type: :development
|
109
|
+
requirement: *id006
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rcov
|
112
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
prerelease: false
|
122
|
+
type: :development
|
123
|
+
requirement: *id007
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: simplecov
|
126
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
hash: 3
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
prerelease: false
|
136
|
+
type: :development
|
137
|
+
requirement: *id008
|
138
|
+
description: A multi-threaded worker pool for Curb
|
139
|
+
email: chetan@pixelcop.net
|
140
|
+
executables: []
|
141
|
+
|
142
|
+
extensions: []
|
143
|
+
|
144
|
+
extra_rdoc_files:
|
145
|
+
- LICENSE
|
146
|
+
- README.md
|
147
|
+
files:
|
148
|
+
- Gemfile
|
149
|
+
- LICENSE
|
150
|
+
- README.md
|
151
|
+
- Rakefile
|
152
|
+
- VERSION
|
153
|
+
- curb_threadpool.gemspec
|
154
|
+
- lib/curb_threadpool.rb
|
155
|
+
- test/helper.rb
|
156
|
+
- test/test_curb_threadpool.rb
|
157
|
+
has_rdoc: true
|
158
|
+
homepage: http://github.com/chetan/curb_threadpool
|
159
|
+
licenses:
|
160
|
+
- BSD
|
161
|
+
post_install_message:
|
162
|
+
rdoc_options: []
|
163
|
+
|
164
|
+
require_paths:
|
165
|
+
- lib
|
166
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
167
|
+
none: false
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
hash: 3
|
172
|
+
segments:
|
173
|
+
- 0
|
174
|
+
version: "0"
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
|
+
none: false
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
hash: 3
|
181
|
+
segments:
|
182
|
+
- 0
|
183
|
+
version: "0"
|
184
|
+
requirements: []
|
185
|
+
|
186
|
+
rubyforge_project:
|
187
|
+
rubygems_version: 1.5.0
|
188
|
+
signing_key:
|
189
|
+
specification_version: 3
|
190
|
+
summary: A multi-threaded worker pool for Curb
|
191
|
+
test_files: []
|
192
|
+
|