curb 0.8.5 → 0.8.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +230 -0
- data/ext/curb.c +7 -0
- data/ext/curb.h +5 -9
- data/ext/curb_easy.c +34 -7
- data/ext/curb_errors.c +27 -16
- data/ext/curb_errors.h +8 -5
- data/ext/curb_multi.c +16 -10
- data/ext/extconf.rb +4 -17
- data/lib/curl.rb +2 -1
- data/lib/curl/easy.rb +69 -59
- data/tests/bug_crash_on_progress.rb +41 -1
- data/tests/bug_issue102.rb +17 -0
- data/tests/helper.rb +6 -1
- data/tests/tc_curl_download.rb +1 -1
- data/tests/tc_curl_easy.rb +11 -4
- data/tests/tc_curl_multi.rb +1 -1
- metadata +7 -5
- data/README +0 -194
data/ext/curb_errors.h
CHANGED
@@ -30,13 +30,13 @@ extern VALUE eCurlErrMalformedURLUser;
|
|
30
30
|
extern VALUE eCurlErrProxyResolution;
|
31
31
|
extern VALUE eCurlErrHostResolution;
|
32
32
|
extern VALUE eCurlErrConnectFailed;
|
33
|
-
extern VALUE
|
33
|
+
extern VALUE eCurlErrFTPWeirdReply;
|
34
34
|
extern VALUE eCurlErrFTPAccessDenied;
|
35
35
|
extern VALUE eCurlErrFTPBadPassword;
|
36
|
-
extern VALUE
|
37
|
-
extern VALUE
|
38
|
-
extern VALUE
|
39
|
-
extern VALUE
|
36
|
+
extern VALUE eCurlErrFTPWeirdPassReply;
|
37
|
+
extern VALUE eCurlErrFTPWeirdUserReply;
|
38
|
+
extern VALUE eCurlErrFTPWeirdPasvReply;
|
39
|
+
extern VALUE eCurlErrFTPWeird227Format;
|
40
40
|
extern VALUE eCurlErrFTPCantGetHost;
|
41
41
|
extern VALUE eCurlErrFTPCantReconnect;
|
42
42
|
extern VALUE eCurlErrFTPCouldntSetBinary;
|
@@ -116,6 +116,9 @@ extern VALUE mCurlErrOutOfMemory;
|
|
116
116
|
extern VALUE mCurlErrInternalError;
|
117
117
|
extern VALUE mCurlErrBadSocket;
|
118
118
|
extern VALUE mCurlErrUnknownOption;
|
119
|
+
#if HAVE_CURLM_ADDED_ALREADY
|
120
|
+
extern VALUE mCurlErrAddedAlready;
|
121
|
+
#endif
|
119
122
|
|
120
123
|
/* binding errors */
|
121
124
|
extern VALUE eCurlErrInvalidPostField;
|
data/ext/curb_multi.c
CHANGED
@@ -4,13 +4,17 @@
|
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
#include "curb_config.h"
|
7
|
-
#
|
8
|
-
|
7
|
+
#include <ruby.h>
|
8
|
+
#ifdef HAVE_RUBY_ST_H
|
9
9
|
#include <ruby/st.h>
|
10
10
|
#else
|
11
|
-
#include <ruby.h>
|
12
11
|
#include <st.h>
|
13
12
|
#endif
|
13
|
+
|
14
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
15
|
+
#include <ruby/thread.h>
|
16
|
+
#endif
|
17
|
+
|
14
18
|
#include "curb_easy.h"
|
15
19
|
#include "curb_errors.h"
|
16
20
|
#include "curb_postfield.h"
|
@@ -64,7 +68,7 @@ rb_hash_clear_i(VALUE key, VALUE value, VALUE dummy) {
|
|
64
68
|
|
65
69
|
static void curl_multi_free(ruby_curl_multi *rbcm) {
|
66
70
|
|
67
|
-
if (rbcm && !rbcm->requests == Qnil && rb_type(rbcm->requests) == T_HASH &&
|
71
|
+
if (rbcm && !rbcm->requests == Qnil && rb_type(rbcm->requests) == T_HASH && RHASH_SIZE(rbcm->requests) > 0) {
|
68
72
|
|
69
73
|
rb_hash_foreach( rbcm->requests, (int (*)())curl_multi_flush_easy, (VALUE)rbcm );
|
70
74
|
|
@@ -262,12 +266,9 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
262
266
|
*/
|
263
267
|
VALUE ruby_curl_multi_remove(VALUE self, VALUE easy) {
|
264
268
|
ruby_curl_multi *rbcm;
|
265
|
-
ruby_curl_easy *rbce;
|
266
269
|
|
267
270
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
268
271
|
|
269
|
-
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
270
|
-
|
271
272
|
rb_curl_multi_remove(rbcm,easy);
|
272
273
|
|
273
274
|
return self;
|
@@ -471,7 +472,7 @@ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
|
|
471
472
|
}
|
472
473
|
#endif
|
473
474
|
|
474
|
-
#
|
475
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
475
476
|
struct _select_set {
|
476
477
|
int maxfd;
|
477
478
|
fd_set *fdread, *fdwrite, *fdexcep;
|
@@ -511,7 +512,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
511
512
|
long timeout_milliseconds;
|
512
513
|
struct timeval tv = {0, 0};
|
513
514
|
VALUE block = Qnil;
|
514
|
-
#
|
515
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
515
516
|
struct _select_set fdset_args;
|
516
517
|
#endif
|
517
518
|
|
@@ -570,17 +571,22 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
570
571
|
create_crt_fd(&fdexcep, &crt_fdexcep);
|
571
572
|
#endif
|
572
573
|
|
573
|
-
#
|
574
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
574
575
|
fdset_args.maxfd = maxfd+1;
|
575
576
|
fdset_args.fdread = &fdread;
|
576
577
|
fdset_args.fdwrite = &fdwrite;
|
577
578
|
fdset_args.fdexcep = &fdexcep;
|
578
579
|
fdset_args.tv = &tv;
|
580
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
581
|
+
rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
582
|
+
#elif HAVE_RB_THREAD_BLOCKING_REGION
|
579
583
|
rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
580
584
|
#else
|
581
585
|
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
582
586
|
#endif
|
583
587
|
|
588
|
+
#endif
|
589
|
+
|
584
590
|
#ifdef _WIN32
|
585
591
|
cleanup_crt_fd(&fdread, &crt_fdread);
|
586
592
|
cleanup_crt_fd(&fdwrite, &crt_fdwrite);
|
data/ext/extconf.rb
CHANGED
@@ -80,6 +80,7 @@ have_constant "curl_version_sspi"
|
|
80
80
|
have_constant "curl_version_conv"
|
81
81
|
have_constant "curlproxy_http"
|
82
82
|
have_constant "curlproxy_socks4"
|
83
|
+
have_constant "curlproxy_socks4a"
|
83
84
|
have_constant "curlproxy_socks5"
|
84
85
|
have_constant "curlauth_basic"
|
85
86
|
have_constant "curlauth_digest"
|
@@ -357,6 +358,8 @@ have_constant "curlopt_gssapi_delegation"
|
|
357
358
|
have_constant "curlgssapi_delegation_policy_flag"
|
358
359
|
have_constant "curlgssapi_delegation_flag"
|
359
360
|
|
361
|
+
have_constant "CURLM_ADDED_ALREADY"
|
362
|
+
|
360
363
|
if try_compile('int main() { return 0; }','-Wall')
|
361
364
|
$CFLAGS << ' -Wall'
|
362
365
|
end
|
@@ -372,23 +375,6 @@ def test_for(name, const, src)
|
|
372
375
|
end
|
373
376
|
end
|
374
377
|
end
|
375
|
-
test_for("Ruby 1.9 Hash", "RUBY19_HASH", %{
|
376
|
-
#include <ruby.h>
|
377
|
-
int main() {
|
378
|
-
VALUE hash = rb_hash_new();
|
379
|
-
if( RHASH(hash)->ntbl->num_entries ) {
|
380
|
-
return 0;
|
381
|
-
}
|
382
|
-
return 1;
|
383
|
-
}
|
384
|
-
})
|
385
|
-
test_for("Ruby 1.9 st.h", "RUBY19_ST_H", %{
|
386
|
-
#include <ruby.h>
|
387
|
-
#include <ruby/st.h>
|
388
|
-
int main() {
|
389
|
-
return 0;
|
390
|
-
}
|
391
|
-
})
|
392
378
|
|
393
379
|
test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
|
394
380
|
#include <curl/curl.h>
|
@@ -400,6 +386,7 @@ test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
|
|
400
386
|
})
|
401
387
|
|
402
388
|
have_func('rb_thread_blocking_region')
|
389
|
+
have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
|
403
390
|
|
404
391
|
create_header('curb_config.h')
|
405
392
|
create_makefile('curb_core')
|
data/lib/curl.rb
CHANGED
@@ -2,6 +2,7 @@ require 'curb_core'
|
|
2
2
|
require 'curl/easy'
|
3
3
|
require 'curl/multi'
|
4
4
|
require 'uri'
|
5
|
+
require 'cgi'
|
5
6
|
|
6
7
|
# expose shortcut methods
|
7
8
|
module Curl
|
@@ -46,7 +47,7 @@ module Curl
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def self.urlalize(url, params={})
|
49
|
-
query_str = params.map {|k,v| "#{URI.escape(k.to_s)}=#{
|
50
|
+
query_str = params.map {|k,v| "#{URI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join('&')
|
50
51
|
if url.match(/\?/)
|
51
52
|
"#{url}&#{query_str}"
|
52
53
|
elsif query_str.size > 0
|
data/lib/curl/easy.rb
CHANGED
@@ -6,31 +6,41 @@ module Curl
|
|
6
6
|
alias body body_str
|
7
7
|
alias head header_str
|
8
8
|
|
9
|
+
class Error < Exception
|
10
|
+
attr_accessor :message, :code
|
11
|
+
def initialize(code, msg)
|
12
|
+
self.message = msg
|
13
|
+
self.code = code
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
9
17
|
#
|
10
18
|
# call-seq:
|
11
19
|
# easy.status => String
|
12
20
|
#
|
13
21
|
def status
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
while parts.size > 0 && parts.first != ''
|
18
|
-
status << parts.shift
|
19
|
-
end
|
20
|
-
status.join(' ')
|
22
|
+
# Matches the last HTTP Status - following the HTTP protocol specification 'Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF'
|
23
|
+
statuses = self.header_str.scan(/HTTP\/\d\.\d\s(\d+\s.+)\r\n/).map{ |match| match[0] }
|
24
|
+
statuses.last.strip
|
21
25
|
end
|
22
26
|
|
23
27
|
#
|
24
28
|
# call-seq:
|
25
29
|
# easy.set :sym|Fixnum, value
|
26
|
-
#
|
30
|
+
#
|
27
31
|
# set options on the curl easy handle see http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
|
28
32
|
#
|
29
33
|
def set(opt,val)
|
30
34
|
if opt.is_a?(Symbol)
|
31
|
-
|
35
|
+
option = sym2curl(opt)
|
32
36
|
else
|
33
|
-
|
37
|
+
option = opt.to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
begin
|
41
|
+
setopt(option, val)
|
42
|
+
rescue TypeError
|
43
|
+
raise TypeError, "Curb doesn't support setting #{opt} [##{option}] option"
|
34
44
|
end
|
35
45
|
end
|
36
46
|
|
@@ -58,8 +68,8 @@ module Curl
|
|
58
68
|
ret = self.multi.perform
|
59
69
|
|
60
70
|
if self.last_result != 0 && self.on_failure.nil?
|
61
|
-
error = Curl::Easy.error(self.last_result)
|
62
|
-
raise error.first
|
71
|
+
error = Curl::Easy.error(self.last_result)
|
72
|
+
raise error.first.new(error.last)
|
63
73
|
end
|
64
74
|
|
65
75
|
ret
|
@@ -83,54 +93,54 @@ module Curl
|
|
83
93
|
# easy.perform
|
84
94
|
#
|
85
95
|
def delete=(onoff)
|
86
|
-
set :customrequest, onoff ? '
|
96
|
+
set :customrequest, onoff ? 'DELETE' : nil
|
87
97
|
onoff
|
88
98
|
end
|
89
|
-
#
|
99
|
+
#
|
90
100
|
# call-seq:
|
91
|
-
#
|
101
|
+
#
|
92
102
|
# easy = Curl::Easy.new("url")
|
93
103
|
# easy.version = Curl::HTTP_1_1
|
94
104
|
# easy.version = Curl::HTTP_1_0
|
95
105
|
# easy.version = Curl::HTTP_NONE
|
96
|
-
#
|
106
|
+
#
|
97
107
|
def version=(http_version)
|
98
108
|
set :http_version, http_version
|
99
109
|
end
|
100
110
|
|
101
|
-
#
|
111
|
+
#
|
102
112
|
# call-seq:
|
103
113
|
# easy.url = "http://some.url/" => "http://some.url/"
|
104
|
-
#
|
114
|
+
#
|
105
115
|
# Set the URL for subsequent calls to +perform+. It is acceptable
|
106
116
|
# (and even recommended) to reuse Curl::Easy instances by reassigning
|
107
117
|
# the URL between calls to +perform+.
|
108
|
-
#
|
118
|
+
#
|
109
119
|
def url=(u)
|
110
120
|
set :url, u
|
111
121
|
end
|
112
122
|
|
113
|
-
#
|
123
|
+
#
|
114
124
|
# call-seq:
|
115
125
|
# easy.proxy_url = string => string
|
116
|
-
#
|
126
|
+
#
|
117
127
|
# Set the URL of the HTTP proxy to use for subsequent calls to +perform+.
|
118
128
|
# The URL should specify the the host name or dotted IP address. To specify
|
119
129
|
# port number in this string, append :[port] to the end of the host name.
|
120
130
|
# The proxy string may be prefixed with [protocol]:// since any such prefix
|
121
131
|
# will be ignored. The proxy's port number may optionally be specified with
|
122
132
|
# the separate option proxy_port .
|
123
|
-
#
|
133
|
+
#
|
124
134
|
# When you tell the library to use an HTTP proxy, libcurl will transparently
|
125
135
|
# convert operations to HTTP even if you specify an FTP URL etc. This may have
|
126
136
|
# an impact on what other features of the library you can use, such as
|
127
137
|
# FTP specifics that don't work unless you tunnel through the HTTP proxy. Such
|
128
138
|
# tunneling is activated with proxy_tunnel = true.
|
129
|
-
#
|
139
|
+
#
|
130
140
|
# libcurl respects the environment variables *http_proxy*, *ftp_proxy*,
|
131
141
|
# *all_proxy* etc, if any of those is set. The proxy_url option does however
|
132
142
|
# override any possibly set environment variables.
|
133
|
-
#
|
143
|
+
#
|
134
144
|
# Starting with libcurl 7.14.1, the proxy host string given in environment
|
135
145
|
# variables can be specified the exact same way as the proxy can be set with
|
136
146
|
# proxy_url, including protocol prefix (http://) and embedded user + password.
|
@@ -145,89 +155,89 @@ module Curl
|
|
145
155
|
self.ssl_verify_host_integer=value
|
146
156
|
end
|
147
157
|
|
148
|
-
#
|
158
|
+
#
|
149
159
|
# call-seq:
|
150
160
|
# easy.ssl_verify_host? => boolean
|
151
|
-
#
|
161
|
+
#
|
152
162
|
# Deprecated: call easy.ssl_verify_host instead
|
153
163
|
# can be one of [0,1,2]
|
154
|
-
#
|
164
|
+
#
|
155
165
|
# Determine whether this Curl instance will verify that the server cert
|
156
166
|
# is for the server it is known as.
|
157
|
-
#
|
167
|
+
#
|
158
168
|
def ssl_verify_host?
|
159
169
|
ssl_verify_host.nil? ? false : (ssl_verify_host > 0)
|
160
170
|
end
|
161
171
|
|
162
|
-
#
|
172
|
+
#
|
163
173
|
# call-seq:
|
164
174
|
# easy.interface = string => string
|
165
|
-
#
|
175
|
+
#
|
166
176
|
# Set the interface name to use as the outgoing network interface.
|
167
177
|
# The name can be an interface name, an IP address or a host name.
|
168
|
-
#
|
178
|
+
#
|
169
179
|
def interface=(value)
|
170
180
|
set :interface, value
|
171
181
|
end
|
172
182
|
|
173
|
-
#
|
183
|
+
#
|
174
184
|
# call-seq:
|
175
185
|
# easy.userpwd = string => string
|
176
|
-
#
|
186
|
+
#
|
177
187
|
# Set the username/password string to use for subsequent calls to +perform+.
|
178
188
|
# The supplied string should have the form "username:password"
|
179
|
-
#
|
189
|
+
#
|
180
190
|
def userpwd=(value)
|
181
191
|
set :userpwd, value
|
182
192
|
end
|
183
193
|
|
184
|
-
#
|
194
|
+
#
|
185
195
|
# call-seq:
|
186
196
|
# easy.proxypwd = string => string
|
187
|
-
#
|
197
|
+
#
|
188
198
|
# Set the username/password string to use for proxy connection during
|
189
199
|
# subsequent calls to +perform+. The supplied string should have the
|
190
200
|
# form "username:password"
|
191
|
-
#
|
201
|
+
#
|
192
202
|
def proxypwd=(value)
|
193
203
|
set :proxyuserpwd, value
|
194
204
|
end
|
195
205
|
|
196
|
-
#
|
206
|
+
#
|
197
207
|
# call-seq:
|
198
208
|
# easy.cookies = "name1=content1; name2=content2;" => string
|
199
|
-
#
|
209
|
+
#
|
200
210
|
# Set cookies to be sent by this Curl::Easy instance. The format of the string should
|
201
211
|
# be NAME=CONTENTS, where NAME is the cookie name and CONTENTS is what the cookie should contain.
|
202
212
|
# Set multiple cookies in one string like this: "name1=content1; name2=content2;" etc.
|
203
|
-
#
|
213
|
+
#
|
204
214
|
def cookies=(value)
|
205
215
|
set :cookie, value
|
206
216
|
end
|
207
217
|
|
208
|
-
#
|
218
|
+
#
|
209
219
|
# call-seq:
|
210
220
|
# easy.cookiefile = string => string
|
211
|
-
#
|
221
|
+
#
|
212
222
|
# Set a file that contains cookies to be sent in subsequent requests by this Curl::Easy instance.
|
213
|
-
#
|
223
|
+
#
|
214
224
|
# *Note* that you must set enable_cookies true to enable the cookie
|
215
225
|
# engine, or this option will be ignored.
|
216
|
-
#
|
226
|
+
#
|
217
227
|
def cookiefile=(value)
|
218
228
|
set :cookiefile, value
|
219
229
|
end
|
220
230
|
|
221
|
-
#
|
231
|
+
#
|
222
232
|
# call-seq:
|
223
233
|
# easy.cookiejar = string => string
|
224
|
-
#
|
234
|
+
#
|
225
235
|
# Set a cookiejar file to use for this Curl::Easy instance.
|
226
236
|
# Cookies from the response will be written into this file.
|
227
|
-
#
|
237
|
+
#
|
228
238
|
# *Note* that you must set enable_cookies true to enable the cookie
|
229
239
|
# engine, or this option will be ignored.
|
230
|
-
#
|
240
|
+
#
|
231
241
|
def cookiejar=(value)
|
232
242
|
set :cookiejar, value
|
233
243
|
end
|
@@ -405,35 +415,35 @@ module Curl
|
|
405
415
|
c.http_delete
|
406
416
|
c
|
407
417
|
end
|
408
|
-
|
418
|
+
|
409
419
|
# call-seq:
|
410
420
|
# Curl::Easy.download(url, filename = url.split(/\?/).first.split(/\//).last) { |curl| ... }
|
411
|
-
#
|
421
|
+
#
|
412
422
|
# Stream the specified url (via perform) and save the data directly to the
|
413
|
-
# supplied filename (defaults to the last component of the URL path, which will
|
423
|
+
# supplied filename (defaults to the last component of the URL path, which will
|
414
424
|
# usually be the filename most simple urls).
|
415
|
-
#
|
425
|
+
#
|
416
426
|
# If a block is supplied, it will be passed the curl instance prior to the
|
417
427
|
# perform call.
|
418
|
-
#
|
428
|
+
#
|
419
429
|
# *Note* that the semantics of the on_body handler are subtly changed when using
|
420
|
-
# download, to account for the automatic routing of data to the specified file: The
|
430
|
+
# download, to account for the automatic routing of data to the specified file: The
|
421
431
|
# data string is passed to the handler *before* it is written
|
422
|
-
# to the file, allowing the handler to perform mutative operations where
|
432
|
+
# to the file, allowing the handler to perform mutative operations where
|
423
433
|
# necessary. As usual, the transfer will be aborted if the on_body handler
|
424
|
-
# returns a size that differs from the data chunk size - in this case, the
|
434
|
+
# returns a size that differs from the data chunk size - in this case, the
|
425
435
|
# offending chunk will *not* be written to the file, the file will be closed,
|
426
436
|
# and a Curl::Err::AbortedByCallbackError will be raised.
|
427
437
|
def download(url, filename = url.split(/\?/).first.split(/\//).last, &blk)
|
428
438
|
curl = Curl::Easy.new(url, &blk)
|
429
|
-
|
439
|
+
|
430
440
|
output = if filename.is_a? IO
|
431
441
|
filename.binmode if filename.respond_to?(:binmode)
|
432
442
|
filename
|
433
443
|
else
|
434
444
|
File.open(filename, 'wb')
|
435
445
|
end
|
436
|
-
|
446
|
+
|
437
447
|
begin
|
438
448
|
old_on_body = curl.on_body do |data|
|
439
449
|
result = old_on_body ? old_on_body.call(data) : data.length
|
@@ -444,7 +454,7 @@ module Curl
|
|
444
454
|
ensure
|
445
455
|
output.close rescue IOError
|
446
456
|
end
|
447
|
-
|
457
|
+
|
448
458
|
return curl
|
449
459
|
end
|
450
460
|
end
|