curb 0.7.15 → 1.0.0
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.
- checksums.yaml +7 -0
- data/README.markdown +283 -0
- data/Rakefile +38 -26
- data/ext/banned.h +32 -0
- data/ext/curb.c +903 -46
- data/ext/curb.h +20 -11
- data/ext/curb_easy.c +1039 -565
- data/ext/curb_easy.h +12 -0
- data/ext/curb_errors.c +127 -18
- data/ext/curb_errors.h +8 -5
- data/ext/curb_macros.h +10 -6
- data/ext/curb_multi.c +245 -167
- data/ext/curb_multi.h +0 -1
- data/ext/curb_upload.c +2 -2
- data/ext/extconf.rb +314 -20
- data/lib/curb.rb +2 -308
- data/lib/curl/easy.rb +489 -0
- data/lib/curl/multi.rb +287 -0
- data/lib/curl.rb +68 -1
- data/tests/bug_crash_on_debug.rb +39 -0
- data/tests/bug_crash_on_progress.rb +73 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +2 -2
- data/tests/bug_issue102.rb +17 -0
- data/tests/bug_require_last_or_segfault.rb +1 -1
- data/tests/helper.rb +120 -16
- data/tests/signals.rb +33 -0
- data/tests/tc_curl.rb +69 -0
- data/tests/tc_curl_download.rb +4 -4
- data/tests/tc_curl_easy.rb +327 -43
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_easy_setopt.rb +31 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +141 -15
- data/tests/tc_curl_postfield.rb +29 -29
- data/tests/tc_curl_protocols.rb +37 -0
- data/tests/timeout.rb +30 -6
- metadata +61 -58
- data/README +0 -177
data/lib/curl/multi.rb
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Curl
|
|
3
|
+
class Multi
|
|
4
|
+
class << self
|
|
5
|
+
# call-seq:
|
|
6
|
+
# Curl::Multi.get(['url1','url2','url3','url4','url5'], :follow_location => true) do|easy|
|
|
7
|
+
# easy
|
|
8
|
+
# end
|
|
9
|
+
#
|
|
10
|
+
# Blocking call to fetch multiple url's in parallel.
|
|
11
|
+
def get(urls, easy_options={}, multi_options={}, &blk)
|
|
12
|
+
url_confs = []
|
|
13
|
+
urls.each do|url|
|
|
14
|
+
url_confs << {:url => url, :method => :get}.merge(easy_options)
|
|
15
|
+
end
|
|
16
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) if blk }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# call-seq:
|
|
20
|
+
#
|
|
21
|
+
# Curl::Multi.post([{:url => 'url1', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
|
|
22
|
+
# {:url => 'url2', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
|
|
23
|
+
# {:url => 'url3', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}}],
|
|
24
|
+
# { :follow_location => true, :multipart_form_post => true },
|
|
25
|
+
# {:pipeline => Curl::CURLPIPE_HTTP1}) do|easy|
|
|
26
|
+
# easy_handle_on_request_complete
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# Blocking call to POST multiple form's in parallel.
|
|
30
|
+
#
|
|
31
|
+
# urls_with_config: is a hash of url's pointing to the postfields to send
|
|
32
|
+
# easy_options: are a set of common options to set on all easy handles
|
|
33
|
+
# multi_options: options to set on the Curl::Multi handle
|
|
34
|
+
#
|
|
35
|
+
def post(urls_with_config, easy_options={}, multi_options={}, &blk)
|
|
36
|
+
url_confs = []
|
|
37
|
+
urls_with_config.each do|uconf|
|
|
38
|
+
url_confs << uconf.merge(:method => :post).merge(easy_options)
|
|
39
|
+
end
|
|
40
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# call-seq:
|
|
44
|
+
#
|
|
45
|
+
# Curl::Multi.put([{:url => 'url1', :put_data => "some message"},
|
|
46
|
+
# {:url => 'url2', :put_data => IO.read('filepath')},
|
|
47
|
+
# {:url => 'url3', :put_data => "maybe another string or socket?"],
|
|
48
|
+
# {:follow_location => true},
|
|
49
|
+
# {:pipeline => Curl::CURLPIPE_HTTP1}) do|easy|
|
|
50
|
+
# easy_handle_on_request_complete
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# Blocking call to POST multiple form's in parallel.
|
|
54
|
+
#
|
|
55
|
+
# urls_with_config: is a hash of url's pointing to the postfields to send
|
|
56
|
+
# easy_options: are a set of common options to set on all easy handles
|
|
57
|
+
# multi_options: options to set on the Curl::Multi handle
|
|
58
|
+
#
|
|
59
|
+
def put(urls_with_config, easy_options={}, multi_options={}, &blk)
|
|
60
|
+
url_confs = []
|
|
61
|
+
urls_with_config.each do|uconf|
|
|
62
|
+
url_confs << uconf.merge(:method => :put).merge(easy_options)
|
|
63
|
+
end
|
|
64
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# call-seq:
|
|
69
|
+
#
|
|
70
|
+
# Curl::Multi.http( [
|
|
71
|
+
# { :url => 'url1', :method => :post,
|
|
72
|
+
# :post_fields => {'field1' => 'value1', 'field2' => 'value2'} },
|
|
73
|
+
# { :url => 'url2', :method => :get,
|
|
74
|
+
# :follow_location => true, :max_redirects => 3 },
|
|
75
|
+
# { :url => 'url3', :method => :put, :put_data => File.open('file.txt','rb') },
|
|
76
|
+
# { :url => 'url4', :method => :head }
|
|
77
|
+
# ], {:pipeline => Curl::CURLPIPE_HTTP1})
|
|
78
|
+
#
|
|
79
|
+
# Blocking call to issue multiple HTTP requests with varying verb's.
|
|
80
|
+
#
|
|
81
|
+
# urls_with_config: is a hash of url's pointing to the easy handle options as well as the special option :method, that can by one of [:get, :post, :put, :delete, :head], when no verb is provided e.g. :method => nil -> GET is used
|
|
82
|
+
# multi_options: options for the multi handle
|
|
83
|
+
# blk: a callback, that yeilds when a handle is completed
|
|
84
|
+
#
|
|
85
|
+
def http(urls_with_config, multi_options={}, &blk)
|
|
86
|
+
m = Curl::Multi.new
|
|
87
|
+
|
|
88
|
+
# maintain a sane number of easy handles
|
|
89
|
+
multi_options[:max_connects] = max_connects = multi_options.key?(:max_connects) ? multi_options[:max_connects] : 10
|
|
90
|
+
|
|
91
|
+
free_handles = [] # keep a list of free easy handles
|
|
92
|
+
|
|
93
|
+
# configure the multi handle
|
|
94
|
+
multi_options.each { |k,v| m.send("#{k}=", v) }
|
|
95
|
+
callbacks = [:on_progress,:on_debug,:on_failure,:on_success,:on_redirect,:on_body,:on_header]
|
|
96
|
+
|
|
97
|
+
add_free_handle = proc do|conf, easy|
|
|
98
|
+
c = conf.dup # avoid being destructive to input
|
|
99
|
+
url = c.delete(:url)
|
|
100
|
+
method = c.delete(:method)
|
|
101
|
+
headers = c.delete(:headers)
|
|
102
|
+
|
|
103
|
+
easy = Curl::Easy.new if easy.nil?
|
|
104
|
+
|
|
105
|
+
easy.url = url
|
|
106
|
+
|
|
107
|
+
# assign callbacks
|
|
108
|
+
callbacks.each do |cb|
|
|
109
|
+
cbproc = c.delete(cb)
|
|
110
|
+
easy.send(cb,&cbproc) if cbproc
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
case method
|
|
114
|
+
when :post
|
|
115
|
+
fields = c.delete(:post_fields)
|
|
116
|
+
# set the post post using the url fields
|
|
117
|
+
easy.post_body = fields.map{|f,k| "#{easy.escape(f)}=#{easy.escape(k)}"}.join('&')
|
|
118
|
+
when :put
|
|
119
|
+
easy.put_data = c.delete(:put_data)
|
|
120
|
+
when :head
|
|
121
|
+
easy.head = true
|
|
122
|
+
when :delete
|
|
123
|
+
easy.delete = true
|
|
124
|
+
when :get
|
|
125
|
+
else
|
|
126
|
+
# XXX: nil is treated like a GET
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# headers is a special key
|
|
130
|
+
headers.each {|k,v| easy.headers[k] = v } if headers
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# use the remaining options as specific configuration to the easy handle
|
|
134
|
+
# bad options should raise an undefined method error
|
|
135
|
+
#
|
|
136
|
+
c.each { |k,v| easy.send("#{k}=",v) }
|
|
137
|
+
|
|
138
|
+
easy.on_complete {|curl|
|
|
139
|
+
free_handles << curl
|
|
140
|
+
blk.call(curl,curl.response_code,method) if blk
|
|
141
|
+
}
|
|
142
|
+
m.add(easy)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
max_connects.times do
|
|
146
|
+
conf = urls_with_config.pop
|
|
147
|
+
add_free_handle.call(conf, nil) if conf
|
|
148
|
+
break if urls_with_config.empty?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
consume_free_handles = proc do
|
|
152
|
+
# as we idle consume free handles
|
|
153
|
+
if urls_with_config.size > 0 && free_handles.size > 0
|
|
154
|
+
easy = free_handles.pop
|
|
155
|
+
conf = urls_with_config.pop
|
|
156
|
+
add_free_handle.call(conf, easy) if conf
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
if urls_with_config.empty?
|
|
161
|
+
m.perform
|
|
162
|
+
else
|
|
163
|
+
until urls_with_config.empty?
|
|
164
|
+
m.perform do
|
|
165
|
+
consume_free_handles.call
|
|
166
|
+
end
|
|
167
|
+
consume_free_handles.call
|
|
168
|
+
end
|
|
169
|
+
free_handles = nil
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# call-seq:
|
|
175
|
+
#
|
|
176
|
+
# Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt']){|c|}
|
|
177
|
+
#
|
|
178
|
+
# will create 2 new files file1.txt and file2.txt
|
|
179
|
+
#
|
|
180
|
+
# 2 files will be opened, and remain open until the call completes
|
|
181
|
+
#
|
|
182
|
+
# when using the :post or :put method, urls should be a hash, including the individual post fields per post
|
|
183
|
+
#
|
|
184
|
+
def download(urls,easy_options={},multi_options={},download_paths=nil,&blk)
|
|
185
|
+
errors = []
|
|
186
|
+
procs = []
|
|
187
|
+
files = []
|
|
188
|
+
urls_with_config = []
|
|
189
|
+
url_to_download_paths = {}
|
|
190
|
+
|
|
191
|
+
urls.each_with_index do|urlcfg,i|
|
|
192
|
+
if urlcfg.is_a?(Hash)
|
|
193
|
+
url = url[:url]
|
|
194
|
+
else
|
|
195
|
+
url = urlcfg
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if download_paths and download_paths[i]
|
|
199
|
+
download_path = download_paths[i]
|
|
200
|
+
else
|
|
201
|
+
download_path = File.basename(url)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
file = lambda do|dp|
|
|
205
|
+
file = File.open(dp,"wb")
|
|
206
|
+
procs << (lambda {|data| file.write data; data.size })
|
|
207
|
+
files << file
|
|
208
|
+
file
|
|
209
|
+
end.call(download_path)
|
|
210
|
+
|
|
211
|
+
if urlcfg.is_a?(Hash)
|
|
212
|
+
urls_with_config << urlcfg.merge({:on_body => procs.last}.merge(easy_options))
|
|
213
|
+
else
|
|
214
|
+
urls_with_config << {:url => url, :on_body => procs.last, :method => :get}.merge(easy_options)
|
|
215
|
+
end
|
|
216
|
+
url_to_download_paths[url] = {:path => download_path, :file => file} # store for later
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
if blk
|
|
220
|
+
# when injecting the block, ensure file is closed before yielding
|
|
221
|
+
Curl::Multi.http(urls_with_config, multi_options) do |c,code,method|
|
|
222
|
+
info = url_to_download_paths[c.url]
|
|
223
|
+
begin
|
|
224
|
+
file = info[:file]
|
|
225
|
+
files.reject!{|f| f == file }
|
|
226
|
+
file.close
|
|
227
|
+
rescue => e
|
|
228
|
+
errors << e
|
|
229
|
+
end
|
|
230
|
+
blk.call(c,info[:path])
|
|
231
|
+
end
|
|
232
|
+
else
|
|
233
|
+
Curl::Multi.http(urls_with_config, multi_options)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
ensure
|
|
237
|
+
files.each {|f|
|
|
238
|
+
begin
|
|
239
|
+
f.close
|
|
240
|
+
rescue => e
|
|
241
|
+
errors << e
|
|
242
|
+
end
|
|
243
|
+
}
|
|
244
|
+
raise errors unless errors.empty?
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def cancel!
|
|
249
|
+
requests.each do |_,easy|
|
|
250
|
+
remove(easy)
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def idle?
|
|
255
|
+
requests.empty?
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def requests
|
|
259
|
+
@requests ||= {}
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def add(easy)
|
|
263
|
+
return self if requests[easy.object_id]
|
|
264
|
+
requests[easy.object_id] = easy
|
|
265
|
+
_add(easy)
|
|
266
|
+
self
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def remove(easy)
|
|
270
|
+
return self if !requests[easy.object_id]
|
|
271
|
+
requests.delete(easy.object_id)
|
|
272
|
+
_remove(easy)
|
|
273
|
+
self
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def close
|
|
277
|
+
requests.values.each {|easy|
|
|
278
|
+
_remove(easy)
|
|
279
|
+
}
|
|
280
|
+
@requests = {}
|
|
281
|
+
_close
|
|
282
|
+
self
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
end
|
|
287
|
+
end
|
data/lib/curl.rb
CHANGED
|
@@ -1 +1,68 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'curb_core'
|
|
3
|
+
require 'curl/easy'
|
|
4
|
+
require 'curl/multi'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'cgi'
|
|
7
|
+
|
|
8
|
+
# expose shortcut methods
|
|
9
|
+
module Curl
|
|
10
|
+
|
|
11
|
+
def self.http(verb, url, post_body=nil, put_data=nil, &block)
|
|
12
|
+
handle = Thread.current[:curb_curl] ||= Curl::Easy.new
|
|
13
|
+
handle.reset
|
|
14
|
+
handle.url = url
|
|
15
|
+
handle.post_body = post_body if post_body
|
|
16
|
+
handle.put_data = put_data if put_data
|
|
17
|
+
yield handle if block_given?
|
|
18
|
+
handle.http(verb)
|
|
19
|
+
handle
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.get(url, params={}, &block)
|
|
23
|
+
http :GET, urlalize(url, params), nil, nil, &block
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.post(url, params={}, &block)
|
|
27
|
+
http :POST, url, postalize(params), nil, &block
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.put(url, params={}, &block)
|
|
31
|
+
http :PUT, url, nil, postalize(params), &block
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.delete(url, params={}, &block)
|
|
35
|
+
http :DELETE, url, postalize(params), nil, &block
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.patch(url, params={}, &block)
|
|
39
|
+
http :PATCH, url, postalize(params), nil, &block
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.head(url, params={}, &block)
|
|
43
|
+
http :HEAD, urlalize(url, params), nil, nil, &block
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.options(url, params={}, &block)
|
|
47
|
+
http :OPTIONS, urlalize(url, params), nil, nil, &block
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.urlalize(url, params={})
|
|
51
|
+
uri = URI(url)
|
|
52
|
+
# early return if we didn't specify any extra params
|
|
53
|
+
return uri.to_s if (params || {}).empty?
|
|
54
|
+
|
|
55
|
+
params_query = URI.encode_www_form(params || {})
|
|
56
|
+
uri.query = [uri.query.to_s, params_query].reject(&:empty?).join('&')
|
|
57
|
+
uri.to_s
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.postalize(params={})
|
|
61
|
+
params.respond_to?(:map) ? URI.encode_www_form(params) : (params.respond_to?(:to_s) ? params.to_s : params)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.reset
|
|
65
|
+
Thread.current[:curb_curl] = Curl::Easy.new
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
|
2
|
+
|
|
3
|
+
require 'webrick'
|
|
4
|
+
class ::WEBrick::HTTPServer ; def access_log(config, req, res) ; end ; end
|
|
5
|
+
class ::WEBrick::BasicLog ; def log(level, data) ; end ; end
|
|
6
|
+
|
|
7
|
+
require 'curl'
|
|
8
|
+
|
|
9
|
+
class BugCrashOnDebug < Test::Unit::TestCase
|
|
10
|
+
|
|
11
|
+
def test_on_debug
|
|
12
|
+
server = WEBrick::HTTPServer.new( :Port => 9999 )
|
|
13
|
+
server.mount_proc("/test") do|req,res|
|
|
14
|
+
res.body = "hi"
|
|
15
|
+
res['Content-Type'] = "text/html"
|
|
16
|
+
end
|
|
17
|
+
puts 'a'
|
|
18
|
+
thread = Thread.new(server) do|srv|
|
|
19
|
+
srv.start
|
|
20
|
+
end
|
|
21
|
+
puts 'b'
|
|
22
|
+
c = Curl::Easy.new('http://127.0.0.1:9999/test')
|
|
23
|
+
c.on_debug do|x|
|
|
24
|
+
puts x.inspect
|
|
25
|
+
raise "error" # this will get swallowed
|
|
26
|
+
end
|
|
27
|
+
c.perform
|
|
28
|
+
puts 'c'
|
|
29
|
+
ensure
|
|
30
|
+
puts 'd'
|
|
31
|
+
server.shutdown
|
|
32
|
+
puts 'e'
|
|
33
|
+
puts thread.exit
|
|
34
|
+
puts 'f'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#test_on_debug
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
|
2
|
+
require 'webrick'
|
|
3
|
+
class ::WEBrick::HTTPServer ; def access_log(config, req, res) ; end ; end
|
|
4
|
+
class ::WEBrick::BasicLog ; def log(level, data) ; end ; end
|
|
5
|
+
|
|
6
|
+
class BugCrashOnDebug < Test::Unit::TestCase
|
|
7
|
+
|
|
8
|
+
def test_on_progress_raise
|
|
9
|
+
server = WEBrick::HTTPServer.new( :Port => 9999 )
|
|
10
|
+
server.mount_proc("/test") do|req,res|
|
|
11
|
+
res.body = "hi"
|
|
12
|
+
res['Content-Type'] = "text/html"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
thread = Thread.new(server) do|srv|
|
|
16
|
+
srv.start
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
c = Curl::Easy.new('http://127.0.0.1:9999/test')
|
|
20
|
+
c.on_progress do|x|
|
|
21
|
+
raise "error"
|
|
22
|
+
end
|
|
23
|
+
c.perform
|
|
24
|
+
|
|
25
|
+
assert false, "should not reach this point"
|
|
26
|
+
|
|
27
|
+
rescue => e
|
|
28
|
+
assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s
|
|
29
|
+
c.close
|
|
30
|
+
ensure
|
|
31
|
+
server.shutdown
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_on_progress_abort
|
|
35
|
+
server = WEBrick::HTTPServer.new( :Port => 9999 )
|
|
36
|
+
server.mount_proc("/test") do|req,res|
|
|
37
|
+
res.body = "hi"
|
|
38
|
+
res['Content-Type'] = "text/html"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
thread = Thread.new(server) do|srv|
|
|
42
|
+
srv.start
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# see: https://github.com/taf2/curb/issues/192,
|
|
46
|
+
# to pass:
|
|
47
|
+
#
|
|
48
|
+
# c = Curl::Easy.new('http://127.0.0.1:9999/test')
|
|
49
|
+
# c.on_progress do|x|
|
|
50
|
+
# puts "we're in the progress callback"
|
|
51
|
+
# false
|
|
52
|
+
# end
|
|
53
|
+
# c.perform
|
|
54
|
+
#
|
|
55
|
+
# notice no return keyword
|
|
56
|
+
#
|
|
57
|
+
c = Curl::Easy.new('http://127.0.0.1:9999/test')
|
|
58
|
+
c.on_progress do|x|
|
|
59
|
+
puts "we're in the progress callback"
|
|
60
|
+
return false
|
|
61
|
+
end
|
|
62
|
+
c.perform
|
|
63
|
+
|
|
64
|
+
assert false, "should not reach this point"
|
|
65
|
+
|
|
66
|
+
rescue => e
|
|
67
|
+
assert_equal 'Curl::Err::AbortedByCallbackError', e.class.to_s
|
|
68
|
+
c.close
|
|
69
|
+
ensure
|
|
70
|
+
server.shutdown
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
@@ -21,7 +21,7 @@ class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase
|
|
|
21
21
|
|
|
22
22
|
5.times do |i|
|
|
23
23
|
t = Thread.new do
|
|
24
|
-
c = Curl::Easy.perform('http://
|
|
24
|
+
c = Curl::Easy.perform('http://127.0.0.1:9999/test')
|
|
25
25
|
c.header_str
|
|
26
26
|
end
|
|
27
27
|
threads << t
|
|
@@ -37,7 +37,7 @@ class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase
|
|
|
37
37
|
timer = Time.now
|
|
38
38
|
single_responses = []
|
|
39
39
|
5.times do |i|
|
|
40
|
-
c = Curl::Easy.perform('http://
|
|
40
|
+
c = Curl::Easy.perform('http://127.0.0.1:9999/test')
|
|
41
41
|
single_responses << c.header_str
|
|
42
42
|
end
|
|
43
43
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
|
2
|
+
|
|
3
|
+
class BugIssue102 < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def test_interface
|
|
6
|
+
test = "https://api.twitter.com/1/users/show.json?screen_name=TwitterAPI&include_entities=true"
|
|
7
|
+
ip = "0.0.0.0"
|
|
8
|
+
|
|
9
|
+
c = Curl::Easy.new do |curl|
|
|
10
|
+
curl.url = test
|
|
11
|
+
curl.interface = ip
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
c.perform
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|