curb 0.7.8 → 0.7.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README +6 -0
- data/ext/curb.h +3 -3
- data/ext/curb_easy.c +109 -16
- data/ext/curb_easy.h +3 -0
- data/ext/curb_multi.c +1 -1
- data/ext/curb_postfield.c +4 -4
- data/ext/curb_upload.c +1 -1
- data/ext/extconf.rb +0 -1
- data/lib/curb.rb +29 -9
- data/tests/helper.rb +1 -0
- data/tests/tc_curl_download.rb +44 -1
- data/tests/tc_curl_easy.rb +39 -6
- data/tests/tc_curl_multi.rb +7 -2
- data/tests/timeout.rb +100 -0
- data/tests/timeout_server.rb +33 -0
- metadata +8 -4
data/README
CHANGED
@@ -156,3 +156,9 @@ documentation with:
|
|
156
156
|
requests.each do|url|
|
157
157
|
puts responses[url]
|
158
158
|
end
|
159
|
+
|
160
|
+
### Easy Callbacks
|
161
|
+
|
162
|
+
on_success: is called when the response code is 20x
|
163
|
+
on_failure: is called when the response code is not success, including redirects e.g. 30x
|
164
|
+
on_complete: is called in all cases.
|
data/ext/curb.h
CHANGED
@@ -20,11 +20,11 @@
|
|
20
20
|
#include "curb_macros.h"
|
21
21
|
|
22
22
|
// These should be managed from the Rake 'release' task.
|
23
|
-
#define CURB_VERSION "0.7.
|
24
|
-
#define CURB_VER_NUM
|
23
|
+
#define CURB_VERSION "0.7.9"
|
24
|
+
#define CURB_VER_NUM 790
|
25
25
|
#define CURB_VER_MAJ 0
|
26
26
|
#define CURB_VER_MIN 7
|
27
|
-
#define CURB_VER_MIC
|
27
|
+
#define CURB_VER_MIC 9
|
28
28
|
#define CURB_VER_PATCH 0
|
29
29
|
|
30
30
|
|
data/ext/curb_easy.c
CHANGED
@@ -137,7 +137,7 @@ static int proc_debug_handler(CURL *curl,
|
|
137
137
|
|
138
138
|
/* ================== MARK/FREE FUNC ==================*/
|
139
139
|
void curl_easy_mark(ruby_curl_easy *rbce) {
|
140
|
-
rb_gc_mark(rbce->opts);
|
140
|
+
if (!NIL_P(rbce->opts)) { rb_gc_mark(rbce->opts); }
|
141
141
|
if (!NIL_P(rbce->multi)) { rb_gc_mark(rbce->multi); }
|
142
142
|
}
|
143
143
|
|
@@ -181,6 +181,8 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
|
|
181
181
|
rbce->connect_timeout = 0;
|
182
182
|
rbce->dns_cache_timeout = 60;
|
183
183
|
rbce->ftp_response_timeout = 0;
|
184
|
+
rbce->low_speed_limit = 0;
|
185
|
+
rbce->low_speed_time = 0;
|
184
186
|
rbce->ssl_version = -1;
|
185
187
|
rbce->use_ssl = -1;
|
186
188
|
rbce->ftp_filemethod = -1;
|
@@ -197,6 +199,7 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
|
|
197
199
|
rbce->verbose = 0;
|
198
200
|
rbce->multipart_form_post = 0;
|
199
201
|
rbce->enable_cookies = 0;
|
202
|
+
rbce->ignore_content_length = 0;
|
200
203
|
}
|
201
204
|
|
202
205
|
/*
|
@@ -225,14 +228,15 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
|
|
225
228
|
rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle");
|
226
229
|
}
|
227
230
|
|
231
|
+
new_curl = Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
|
232
|
+
|
228
233
|
rbce->multi = Qnil;
|
234
|
+
rbce->opts = Qnil;
|
229
235
|
|
230
236
|
ruby_curl_easy_zero(rbce);
|
231
237
|
|
232
238
|
rb_easy_set("url", url);
|
233
239
|
|
234
|
-
new_curl = Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
|
235
|
-
|
236
240
|
/* set the new_curl pointer to the curl handle */
|
237
241
|
ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)new_curl);
|
238
242
|
if (ecode != CURLE_OK) {
|
@@ -331,6 +335,12 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
|
|
331
335
|
raise_curl_easy_error_exception(ecode);
|
332
336
|
}
|
333
337
|
|
338
|
+
/* Free everything up */
|
339
|
+
if (rbce->curl_headers) {
|
340
|
+
curl_slist_free_all(rbce->curl_headers);
|
341
|
+
rbce->curl_headers = NULL;
|
342
|
+
}
|
343
|
+
|
334
344
|
return opts_dup;
|
335
345
|
}
|
336
346
|
|
@@ -826,7 +836,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
|
|
826
836
|
rb_hash_aset(headers, rb_str_new2("Expect"), rb_str_new2(""));
|
827
837
|
}
|
828
838
|
size = rb_funcall(stat, rb_intern("size"), 0);
|
829
|
-
curl_easy_setopt(curl, CURLOPT_INFILESIZE,
|
839
|
+
curl_easy_setopt(curl, CURLOPT_INFILESIZE, FIX2LONG(size));
|
830
840
|
}
|
831
841
|
else if( rb_hash_aref(headers, rb_str_new2("Transfer-Encoding")) == Qnil ) {
|
832
842
|
rb_hash_aset(headers, rb_str_new2("Transfer-Encoding"), rb_str_new2("chunked"));
|
@@ -1002,7 +1012,8 @@ static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE sel
|
|
1002
1012
|
|
1003
1013
|
rb_scan_args(argc, argv, "*", &args_ary);
|
1004
1014
|
Data_Get_Struct(self, ruby_curl_easy, rbce);
|
1005
|
-
|
1015
|
+
|
1016
|
+
len = (int)RARRAY_LEN(args_ary);
|
1006
1017
|
|
1007
1018
|
if (len == 1 && (TYPE(rb_ary_entry(args_ary,0)) == T_FIXNUM || rb_ary_entry(args_ary,0) == Qnil)) {
|
1008
1019
|
if (rb_ary_entry(args_ary,0) == Qnil) {
|
@@ -1188,6 +1199,51 @@ static VALUE ruby_curl_easy_ftp_response_timeout_get(VALUE self, VALUE ftp_respo
|
|
1188
1199
|
CURB_IMMED_GETTER(ruby_curl_easy, ftp_response_timeout, 0);
|
1189
1200
|
}
|
1190
1201
|
|
1202
|
+
/*
|
1203
|
+
* call-seq:
|
1204
|
+
* easy.low_speed_limit = fixnum or nil => fixnum or nil
|
1205
|
+
*
|
1206
|
+
* Set the transfer speed (in bytes per second) that the transfer should be
|
1207
|
+
* below during +low_speed_time+ seconds for the library to consider it too
|
1208
|
+
* slow and abort.
|
1209
|
+
*/
|
1210
|
+
static VALUE ruby_curl_easy_low_speed_limit_set(VALUE self, VALUE low_speed_limit) {
|
1211
|
+
CURB_IMMED_SETTER(ruby_curl_easy, low_speed_limit, 0);
|
1212
|
+
}
|
1213
|
+
|
1214
|
+
/*
|
1215
|
+
* call-seq:
|
1216
|
+
* easy.low_speed_limit => fixnum or nil
|
1217
|
+
*
|
1218
|
+
* Obtain the minimum transfer speed over +low_speed+time+ below which the
|
1219
|
+
* transfer will be aborted.
|
1220
|
+
*/
|
1221
|
+
static VALUE ruby_curl_easy_low_speed_limit_get(VALUE self, VALUE low_speed_limit) {
|
1222
|
+
CURB_IMMED_GETTER(ruby_curl_easy, low_speed_limit, 0);
|
1223
|
+
}
|
1224
|
+
|
1225
|
+
/*
|
1226
|
+
* call-seq:
|
1227
|
+
* easy.low_speed_time = fixnum or nil => fixnum or nil
|
1228
|
+
*
|
1229
|
+
* Set the time (in seconds) that the transfer should be below the
|
1230
|
+
* +low_speed_limit+ for the library to consider it too slow and abort.
|
1231
|
+
*/
|
1232
|
+
static VALUE ruby_curl_easy_low_speed_time_set(VALUE self, VALUE low_speed_time) {
|
1233
|
+
CURB_IMMED_SETTER(ruby_curl_easy, low_speed_time, 0);
|
1234
|
+
}
|
1235
|
+
|
1236
|
+
/*
|
1237
|
+
* call-seq:
|
1238
|
+
* easy.low_speed_time => fixnum or nil
|
1239
|
+
*
|
1240
|
+
* Obtain the time that the transfer should be below +low_speed_limit+ for
|
1241
|
+
* the library to abort it.
|
1242
|
+
*/
|
1243
|
+
static VALUE ruby_curl_easy_low_speed_time_get(VALUE self, VALUE low_speed_time) {
|
1244
|
+
CURB_IMMED_GETTER(ruby_curl_easy, low_speed_time, 0);
|
1245
|
+
}
|
1246
|
+
|
1191
1247
|
/*
|
1192
1248
|
* call-seq:
|
1193
1249
|
* easy.username = string => string
|
@@ -1425,8 +1481,8 @@ static VALUE ruby_curl_easy_header_in_body_set(VALUE self, VALUE header_in_body)
|
|
1425
1481
|
* call-seq:
|
1426
1482
|
* easy.header_in_body? => boolean
|
1427
1483
|
*
|
1428
|
-
* Determine whether this Curl instance will
|
1429
|
-
*
|
1484
|
+
* Determine whether this Curl instance will return HTTP headers
|
1485
|
+
* combined with body data.
|
1430
1486
|
*/
|
1431
1487
|
static VALUE ruby_curl_easy_header_in_body_q(VALUE self) {
|
1432
1488
|
CURB_BOOLEAN_GETTER(ruby_curl_easy, header_in_body);
|
@@ -1592,6 +1648,31 @@ static VALUE ruby_curl_easy_enable_cookies_q(VALUE self) {
|
|
1592
1648
|
CURB_BOOLEAN_GETTER(ruby_curl_easy, enable_cookies);
|
1593
1649
|
}
|
1594
1650
|
|
1651
|
+
/*
|
1652
|
+
* call-seq:
|
1653
|
+
* easy.ignore_content_length = boolean
|
1654
|
+
*
|
1655
|
+
* Configure whether this Curl::Easy instance should ignore the content
|
1656
|
+
* length header.
|
1657
|
+
*/
|
1658
|
+
static VALUE ruby_curl_easy_ignore_content_length_set(VALUE self, VALUE ignore_content_length)
|
1659
|
+
{
|
1660
|
+
CURB_BOOLEAN_SETTER(ruby_curl_easy, ignore_content_length);
|
1661
|
+
}
|
1662
|
+
|
1663
|
+
/*
|
1664
|
+
* call-seq:
|
1665
|
+
* easy.ignore_content_length? => boolean
|
1666
|
+
*
|
1667
|
+
* Determine whether this Curl::Easy instance ignores the content
|
1668
|
+
* length header.
|
1669
|
+
*/
|
1670
|
+
static VALUE ruby_curl_easy_ignore_content_length_q(VALUE self) {
|
1671
|
+
CURB_BOOLEAN_GETTER(ruby_curl_easy, ignore_content_length);
|
1672
|
+
}
|
1673
|
+
|
1674
|
+
|
1675
|
+
|
1595
1676
|
/* ================= EVENT PROCS ================== */
|
1596
1677
|
|
1597
1678
|
/*
|
@@ -1895,6 +1976,9 @@ VALUE ruby_curl_easy_setup( ruby_curl_easy *rbce ) {
|
|
1895
1976
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout);
|
1896
1977
|
curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout);
|
1897
1978
|
|
1979
|
+
curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, rbce->ignore_content_length);
|
1980
|
+
|
1981
|
+
|
1898
1982
|
#if LIBCURL_VERSION_NUM >= 0x070a08
|
1899
1983
|
curl_easy_setopt(curl, CURLOPT_FTP_RESPONSE_TIMEOUT, rbce->ftp_response_timeout);
|
1900
1984
|
#else
|
@@ -1903,6 +1987,9 @@ VALUE ruby_curl_easy_setup( ruby_curl_easy *rbce ) {
|
|
1903
1987
|
}
|
1904
1988
|
#endif
|
1905
1989
|
|
1990
|
+
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, rbce->low_speed_limit);
|
1991
|
+
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, rbce->low_speed_time);
|
1992
|
+
|
1906
1993
|
// Set up localport / proxy port
|
1907
1994
|
// FIXME these won't get returned to default if they're unset Ruby
|
1908
1995
|
if (rbce->proxy_port > 0) {
|
@@ -2054,10 +2141,10 @@ VALUE ruby_curl_easy_setup( ruby_curl_easy *rbce ) {
|
|
2054
2141
|
VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
|
2055
2142
|
|
2056
2143
|
CURL *curl = rbce->curl;
|
2057
|
-
|
2058
|
-
|
2059
|
-
if (
|
2060
|
-
curl_slist_free_all(
|
2144
|
+
|
2145
|
+
/* Free everything up */
|
2146
|
+
if (rbce->curl_headers) {
|
2147
|
+
curl_slist_free_all(rbce->curl_headers);
|
2061
2148
|
rbce->curl_headers = NULL;
|
2062
2149
|
}
|
2063
2150
|
|
@@ -2067,7 +2154,7 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
|
|
2067
2154
|
rbce->curl_ftp_commands = NULL;
|
2068
2155
|
}
|
2069
2156
|
|
2070
|
-
|
2157
|
+
/* clean up a PUT request's curl options. */
|
2071
2158
|
if (!rb_easy_nil("upload")) {
|
2072
2159
|
rb_easy_del("upload"); // set the upload object to Qnil to let the GC clean up
|
2073
2160
|
curl_easy_setopt(curl, CURLOPT_UPLOAD, 0);
|
@@ -2132,7 +2219,7 @@ static VALUE ruby_curl_easy_perform_get(VALUE self) {
|
|
2132
2219
|
/*
|
2133
2220
|
* Common implementation of easy.http(verb) and easy.http_delete
|
2134
2221
|
*/
|
2135
|
-
static VALUE ruby_curl_easy_perform_verb_str(VALUE self, char *verb) {
|
2222
|
+
static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
|
2136
2223
|
ruby_curl_easy *rbce;
|
2137
2224
|
CURL *curl;
|
2138
2225
|
VALUE retval;
|
@@ -3042,9 +3129,9 @@ static VALUE ruby_curl_easy_escape(VALUE self, VALUE svalue) {
|
|
3042
3129
|
if( rb_type(str) != T_STRING ) { str = rb_funcall(str,rb_intern("to_s"),0); }
|
3043
3130
|
|
3044
3131
|
#if (LIBCURL_VERSION_NUM >= 0x070f04)
|
3045
|
-
result = (char*)curl_easy_escape(rbce->curl, StringValuePtr(str), RSTRING_LEN(str));
|
3132
|
+
result = (char*)curl_easy_escape(rbce->curl, StringValuePtr(str), (int)RSTRING_LEN(str));
|
3046
3133
|
#else
|
3047
|
-
result = (char*)curl_escape(StringValuePtr(str), RSTRING_LEN(str));
|
3134
|
+
result = (char*)curl_escape(StringValuePtr(str), (int)RSTRING_LEN(str));
|
3048
3135
|
#endif
|
3049
3136
|
|
3050
3137
|
rresult = rb_str_new2(result);
|
@@ -3070,7 +3157,7 @@ static VALUE ruby_curl_easy_unescape(VALUE self, VALUE str) {
|
|
3070
3157
|
Data_Get_Struct(self, ruby_curl_easy, rbce);
|
3071
3158
|
|
3072
3159
|
#if (LIBCURL_VERSION_NUM >= 0x070f04)
|
3073
|
-
result = (char*)curl_easy_unescape(rbce->curl, StringValuePtr(str), RSTRING_LEN(str), &rlen);
|
3160
|
+
result = (char*)curl_easy_unescape(rbce->curl, StringValuePtr(str), (int)RSTRING_LEN(str), &rlen);
|
3074
3161
|
#else
|
3075
3162
|
result = (char*)curl_unescape(StringValuePtr(str), RSTRING_LEN(str));
|
3076
3163
|
rlen = strlen(result);
|
@@ -3274,6 +3361,10 @@ void init_curb_easy() {
|
|
3274
3361
|
rb_define_method(cCurlEasy, "dns_cache_timeout", ruby_curl_easy_dns_cache_timeout_get, 0);
|
3275
3362
|
rb_define_method(cCurlEasy, "ftp_response_timeout=", ruby_curl_easy_ftp_response_timeout_set, 1);
|
3276
3363
|
rb_define_method(cCurlEasy, "ftp_response_timeout", ruby_curl_easy_ftp_response_timeout_get, 0);
|
3364
|
+
rb_define_method(cCurlEasy, "low_speed_limit=", ruby_curl_easy_low_speed_limit_set, 1);
|
3365
|
+
rb_define_method(cCurlEasy, "low_speed_limit", ruby_curl_easy_low_speed_limit_get, 0);
|
3366
|
+
rb_define_method(cCurlEasy, "low_speed_time=", ruby_curl_easy_low_speed_time_set, 1);
|
3367
|
+
rb_define_method(cCurlEasy, "low_speed_time", ruby_curl_easy_low_speed_time_get, 0);
|
3277
3368
|
rb_define_method(cCurlEasy, "ssl_version=", ruby_curl_easy_ssl_version_set, 1);
|
3278
3369
|
rb_define_method(cCurlEasy, "ssl_version", ruby_curl_easy_ssl_version_get, 0);
|
3279
3370
|
rb_define_method(cCurlEasy, "use_ssl=", ruby_curl_easy_use_ssl_set, 1);
|
@@ -3309,6 +3400,8 @@ void init_curb_easy() {
|
|
3309
3400
|
rb_define_method(cCurlEasy, "multipart_form_post?", ruby_curl_easy_multipart_form_post_q, 0);
|
3310
3401
|
rb_define_method(cCurlEasy, "enable_cookies=", ruby_curl_easy_enable_cookies_set, 1);
|
3311
3402
|
rb_define_method(cCurlEasy, "enable_cookies?", ruby_curl_easy_enable_cookies_q, 0);
|
3403
|
+
rb_define_method(cCurlEasy, "ignore_content_length=", ruby_curl_easy_ignore_content_length_set, 1);
|
3404
|
+
rb_define_method(cCurlEasy, "ignore_content_length?", ruby_curl_easy_ignore_content_length_q, 0);
|
3312
3405
|
|
3313
3406
|
rb_define_method(cCurlEasy, "on_body", ruby_curl_easy_on_body_set, -1);
|
3314
3407
|
rb_define_method(cCurlEasy, "on_header", ruby_curl_easy_on_header_set, -1);
|
data/ext/curb_easy.h
CHANGED
@@ -51,6 +51,8 @@ typedef struct {
|
|
51
51
|
unsigned long connect_timeout;
|
52
52
|
long dns_cache_timeout;
|
53
53
|
unsigned long ftp_response_timeout;
|
54
|
+
long low_speed_limit;
|
55
|
+
long low_speed_time;
|
54
56
|
long ssl_version;
|
55
57
|
long use_ssl;
|
56
58
|
long ftp_filemethod;
|
@@ -67,6 +69,7 @@ typedef struct {
|
|
67
69
|
char verbose;
|
68
70
|
char multipart_form_post;
|
69
71
|
char enable_cookies;
|
72
|
+
char ignore_content_length;
|
70
73
|
struct curl_slist *curl_headers;
|
71
74
|
struct curl_slist *curl_ftp_commands;
|
72
75
|
|
data/ext/curb_multi.c
CHANGED
@@ -456,7 +456,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
456
456
|
}
|
457
457
|
|
458
458
|
tv.tv_sec = 0; /* never wait longer than 1 second */
|
459
|
-
tv.tv_usec = timeout_milliseconds * 1000;
|
459
|
+
tv.tv_usec = (int)(timeout_milliseconds * 1000); /* XXX: int is the right type for OSX, what about linux? */
|
460
460
|
|
461
461
|
if (timeout_milliseconds == 0) { /* no delay */
|
462
462
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
data/ext/curb_postfield.c
CHANGED
@@ -109,7 +109,7 @@ void append_to_form(VALUE self,
|
|
109
109
|
} else {
|
110
110
|
// is a content field
|
111
111
|
if (rbcpf->content_proc != Qnil) {
|
112
|
-
rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, self);
|
112
|
+
rbcpf->buffer_str = rb_funcall(rbcpf->content_proc, idCall, 1, self);
|
113
113
|
|
114
114
|
if (rbcpf->content_type == Qnil) {
|
115
115
|
result = curl_formadd(first, last, CURLFORM_PTRNAME, StringValuePtr(rbcpf->name),
|
@@ -209,7 +209,7 @@ void curl_postfield_free(ruby_curl_postfield *rbcpf) {
|
|
209
209
|
*/
|
210
210
|
static VALUE ruby_curl_postfield_new_content(int argc, VALUE *argv, VALUE klass) {
|
211
211
|
ruby_curl_postfield *rbcpf = ALLOC(ruby_curl_postfield);
|
212
|
-
|
212
|
+
|
213
213
|
// wierdness - we actually require two args, unless a block is provided, but
|
214
214
|
// we have to work that out below.
|
215
215
|
rb_scan_args(argc, argv, "12&", &rbcpf->name, &rbcpf->content, &rbcpf->content_type, &rbcpf->content_proc);
|
@@ -443,7 +443,7 @@ static VALUE ruby_curl_postfield_to_str(VALUE self) {
|
|
443
443
|
rb_raise(eCurlErrInvalidPostField, "Cannot convert unnamed field to string %s:%d, make sure your field name responds_to :to_s", __FILE__, __LINE__);
|
444
444
|
}
|
445
445
|
|
446
|
-
char *tmpchrs = curl_escape(StringValuePtr(name), RSTRING_LEN(name));
|
446
|
+
char *tmpchrs = curl_escape(StringValuePtr(name), (int)RSTRING_LEN(name));
|
447
447
|
|
448
448
|
if (!tmpchrs) {
|
449
449
|
rb_raise(eCurlErrInvalidPostField, "Failed to url-encode name `%s'", tmpchrs);
|
@@ -472,7 +472,7 @@ static VALUE ruby_curl_postfield_to_str(VALUE self) {
|
|
472
472
|
}
|
473
473
|
}
|
474
474
|
//fprintf(stderr, "encoding content: %ld - %s\n", RSTRING_LEN(tmpcontent), RSTRING_PTR(tmpcontent) );
|
475
|
-
tmpchrs = curl_escape(RSTRING_PTR(tmpcontent), RSTRING_LEN(tmpcontent));
|
475
|
+
tmpchrs = curl_escape(RSTRING_PTR(tmpcontent), (int)RSTRING_LEN(tmpcontent));
|
476
476
|
if (!tmpchrs) {
|
477
477
|
rb_raise(eCurlErrInvalidPostField, "Failed to url-encode content `%s'", tmpchrs);
|
478
478
|
} else {
|
data/ext/curb_upload.c
CHANGED
@@ -56,7 +56,7 @@ VALUE ruby_curl_upload_stream_get(VALUE self) {
|
|
56
56
|
VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset) {
|
57
57
|
ruby_curl_upload *rbcu;
|
58
58
|
Data_Get_Struct(self, ruby_curl_upload, rbcu);
|
59
|
-
rbcu->offset =
|
59
|
+
rbcu->offset = FIX2LONG(offset);
|
60
60
|
return offset;
|
61
61
|
}
|
62
62
|
/*
|
data/ext/extconf.rb
CHANGED
data/lib/curb.rb
CHANGED
@@ -26,16 +26,24 @@ module Curl
|
|
26
26
|
def download(url, filename = url.split(/\?/).first.split(/\//).last, &blk)
|
27
27
|
curl = Curl::Easy.new(url, &blk)
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
output = if filename.is_a? IO
|
30
|
+
filename.binmode if filename.respond_to?(:binmode)
|
31
|
+
filename
|
32
|
+
else
|
33
|
+
File.open(filename, 'wb')
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
old_on_body = curl.on_body do |data|
|
31
38
|
result = old_on_body ? old_on_body.call(data) : data.length
|
32
39
|
output << data if result == data.length
|
33
40
|
result
|
34
41
|
end
|
35
|
-
|
36
42
|
curl.perform
|
37
|
-
|
38
|
-
|
43
|
+
ensure
|
44
|
+
output.close rescue IOError
|
45
|
+
end
|
46
|
+
|
39
47
|
return curl
|
40
48
|
end
|
41
49
|
end
|
@@ -200,6 +208,7 @@ module Curl
|
|
200
208
|
# when using the :post or :put method, urls should be a hash, including the individual post fields per post
|
201
209
|
#
|
202
210
|
def download(urls,easy_options={},multi_options={},download_paths=nil,&blk)
|
211
|
+
errors = []
|
203
212
|
procs = []
|
204
213
|
files = []
|
205
214
|
urls_with_config = []
|
@@ -218,10 +227,11 @@ module Curl
|
|
218
227
|
download_path = File.basename(url)
|
219
228
|
end
|
220
229
|
|
221
|
-
lambda do|dp|
|
230
|
+
file = lambda do|dp|
|
222
231
|
file = File.open(dp,"wb")
|
223
232
|
procs << (lambda {|data| file.write data; data.size })
|
224
233
|
files << file
|
234
|
+
file
|
225
235
|
end.call(download_path)
|
226
236
|
|
227
237
|
if urlcfg.is_a?(Hash)
|
@@ -229,17 +239,27 @@ module Curl
|
|
229
239
|
else
|
230
240
|
urls_with_config << {:url => url, :on_body => procs.last, :method => :get}.merge(easy_options)
|
231
241
|
end
|
232
|
-
url_to_download_paths[url] = download_path # store for later
|
242
|
+
url_to_download_paths[url] = {:path => download_path, :file => file} # store for later
|
233
243
|
end
|
234
244
|
|
235
245
|
if blk
|
236
|
-
|
246
|
+
# when injecting the block, ensure file is closed before yielding
|
247
|
+
Curl::Multi.http(urls_with_config, multi_options) do |c,code,method|
|
248
|
+
info = url_to_download_paths[c.url]
|
249
|
+
begin
|
250
|
+
file = info[:file]
|
251
|
+
files.reject!{|f| f == file }
|
252
|
+
file.close
|
253
|
+
rescue => e
|
254
|
+
errors << e
|
255
|
+
end
|
256
|
+
blk.call(c,info[:path])
|
257
|
+
end
|
237
258
|
else
|
238
259
|
Curl::Multi.http(urls_with_config, multi_options)
|
239
260
|
end
|
240
261
|
|
241
262
|
ensure
|
242
|
-
errors = []
|
243
263
|
files.each {|f|
|
244
264
|
begin
|
245
265
|
f.close
|
data/tests/helper.rb
CHANGED
data/tests/tc_curl_download.rb
CHANGED
@@ -7,7 +7,7 @@ class TestCurbCurlDownload < Test::Unit::TestCase
|
|
7
7
|
server_setup
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def test_download_url_to_file_via_string
|
11
11
|
dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
|
12
12
|
dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
|
13
13
|
|
@@ -18,6 +18,49 @@ class TestCurbCurlDownload < Test::Unit::TestCase
|
|
18
18
|
File.unlink(dl_path) if File.exist?(dl_path)
|
19
19
|
end
|
20
20
|
|
21
|
+
def test_download_url_to_file_via_file_io
|
22
|
+
dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
|
23
|
+
dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
|
24
|
+
io = File.open(dl_path, 'wb')
|
25
|
+
|
26
|
+
curb = Curl::Easy.download(dl_url, io)
|
27
|
+
assert io.closed?
|
28
|
+
assert File.exist?(dl_path)
|
29
|
+
assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path)
|
30
|
+
ensure
|
31
|
+
File.unlink(dl_path) if File.exist?(dl_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_download_url_to_file_via_io
|
35
|
+
dl_url = "http://127.0.0.1:9129/ext/curb_easy.c"
|
36
|
+
dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
|
37
|
+
reader, writer = IO.pipe
|
38
|
+
|
39
|
+
# Write to local file
|
40
|
+
fork do
|
41
|
+
begin
|
42
|
+
writer.close
|
43
|
+
File.open(dl_path, 'wb') { |file| file << reader.read }
|
44
|
+
ensure
|
45
|
+
reader.close rescue IOError # if the stream has already been closed
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Download remote source
|
50
|
+
begin
|
51
|
+
reader.close
|
52
|
+
curb = Curl::Easy.download(dl_url, writer)
|
53
|
+
Process.wait
|
54
|
+
ensure
|
55
|
+
writer.close rescue IOError # if the stream has already been closed, which occurs in Easy::download
|
56
|
+
end
|
57
|
+
|
58
|
+
assert File.exist?(dl_path)
|
59
|
+
assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(dl_path)
|
60
|
+
ensure
|
61
|
+
File.unlink(dl_path) if File.exist?(dl_path)
|
62
|
+
end
|
63
|
+
|
21
64
|
def test_download_bad_url_gives_404
|
22
65
|
dl_url = "http://127.0.0.1:9129/this_file_does_not_exist.html"
|
23
66
|
dl_path = File.join(Dir::tmpdir, "dl_url_test.file")
|
data/tests/tc_curl_easy.rb
CHANGED
@@ -354,6 +354,30 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
354
354
|
assert_equal 30, c.dns_cache_timeout
|
355
355
|
end
|
356
356
|
|
357
|
+
def test_low_speed_limit_01
|
358
|
+
c = Curl::Easy.new($TEST_URL)
|
359
|
+
|
360
|
+
assert_nil c.low_speed_limit
|
361
|
+
|
362
|
+
c.low_speed_limit = 3
|
363
|
+
assert_equal 3, c.low_speed_limit
|
364
|
+
|
365
|
+
c.low_speed_limit = nil
|
366
|
+
assert_nil c.low_speed_limit
|
367
|
+
end
|
368
|
+
|
369
|
+
def test_low_speed_time_01
|
370
|
+
c = Curl::Easy.new($TEST_URL)
|
371
|
+
|
372
|
+
assert_nil c.low_speed_time
|
373
|
+
|
374
|
+
c.low_speed_time = 3
|
375
|
+
assert_equal 3, c.low_speed_time
|
376
|
+
|
377
|
+
c.low_speed_time = nil
|
378
|
+
assert_nil c.low_speed_time
|
379
|
+
end
|
380
|
+
|
357
381
|
def test_on_body
|
358
382
|
blk = lambda { |i| i.length }
|
359
383
|
|
@@ -476,6 +500,13 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
476
500
|
assert c.multipart_form_post?
|
477
501
|
end
|
478
502
|
|
503
|
+
def test_ignore_content_length
|
504
|
+
c = Curl::Easy.new
|
505
|
+
assert !c.ignore_content_length?
|
506
|
+
assert c.ignore_content_length = true
|
507
|
+
assert c.ignore_content_length?
|
508
|
+
end
|
509
|
+
|
479
510
|
def test_enable_cookies
|
480
511
|
c = Curl::Easy.new
|
481
512
|
assert !c.enable_cookies?
|
@@ -664,24 +695,26 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
664
695
|
def test_cert
|
665
696
|
curl = Curl::Easy.new(TestServlet.url)
|
666
697
|
curl.cert= File.join(File.dirname(__FILE__),"cert.pem")
|
667
|
-
|
698
|
+
assert_match /cert.pem$/,curl.cert
|
668
699
|
end
|
669
700
|
|
670
701
|
def test_cert_with_password
|
671
702
|
curl = Curl::Easy.new(TestServlet.url)
|
672
703
|
curl.cert= File.join(File.dirname(__FILE__),"cert.pem:password")
|
673
|
-
|
704
|
+
assert_match /cert.pem$/,curl.cert
|
674
705
|
end
|
675
706
|
|
676
707
|
def test_cert_type
|
677
708
|
curl = Curl::Easy.new(TestServlet.url)
|
678
709
|
curl.certtype= "DER"
|
679
|
-
|
710
|
+
assert_equal "DER", curl.certtype
|
680
711
|
end
|
681
712
|
|
682
713
|
def test_default_certtype
|
683
714
|
curl = Curl::Easy.new(TestServlet.url)
|
684
|
-
|
715
|
+
assert_nil curl.certtype
|
716
|
+
curl.certtype = "PEM"
|
717
|
+
assert_equal "PEM", curl.certtype
|
685
718
|
end
|
686
719
|
|
687
720
|
# Generate a CA cert with instructions at
|
@@ -689,13 +722,13 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
689
722
|
def test_ca_cert
|
690
723
|
curl = Curl::Easy.new(TestServlet.url)
|
691
724
|
curl.cacert= File.join(File.dirname(__FILE__),"cacert.pem")
|
692
|
-
|
725
|
+
assert_match /cacert.pem$/, curl.cacert
|
693
726
|
end
|
694
727
|
|
695
728
|
def test_user_agent
|
696
729
|
curl = Curl::Easy.new(TestServlet.url)
|
697
730
|
curl.useragent= "Curb-Easy/Ruby"
|
698
|
-
|
731
|
+
assert_equal "Curb-Easy/Ruby",curl.useragent
|
699
732
|
end
|
700
733
|
|
701
734
|
def test_username_password
|
data/tests/tc_curl_multi.rb
CHANGED
@@ -315,15 +315,20 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
315
315
|
downloads = []
|
316
316
|
file_info = {}
|
317
317
|
FileUtils.mkdir("tmp/")
|
318
|
+
|
319
|
+
# for each file store the size by file name
|
318
320
|
Dir[File.dirname(__FILE__) + "/../ext/*.c"].each do|path|
|
319
321
|
urls << (root_uri + File.basename(path))
|
320
322
|
downloads << "tmp/" + File.basename(path)
|
321
|
-
file_info[File.basename(path)] = {:size => File.size(path)}
|
323
|
+
file_info[File.basename(path)] = {:size => File.size(path), :path => path}
|
322
324
|
end
|
325
|
+
|
326
|
+
# start downloads
|
323
327
|
Curl::Multi.download(urls,{},{},downloads) do|curl,download_path|
|
324
328
|
assert_equal 200, curl.response_code
|
325
329
|
assert File.exist?(download_path)
|
326
|
-
|
330
|
+
store = file_info[File.basename(download_path)]
|
331
|
+
assert_equal file_info[File.basename(download_path)][:size], File.size(download_path), "incomplete download: #{download_path}"
|
327
332
|
end
|
328
333
|
ensure
|
329
334
|
FileUtils.rm_rf("tmp/")
|
data/tests/timeout.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
2
|
+
|
3
|
+
# Run server with: ruby -rubygems timeout_server.rb -p 9128
|
4
|
+
|
5
|
+
# Note that curl requires all timeouts to be integers -
|
6
|
+
# curl_easy_setopt does not have a provision for floating-point values
|
7
|
+
|
8
|
+
class TestCurbTimeouts < Test::Unit::TestCase
|
9
|
+
def test_no_timeout_by_default
|
10
|
+
curl = Curl::Easy.new(wait_url(2))
|
11
|
+
start = Time.now
|
12
|
+
assert_equal true, curl.http_get
|
13
|
+
elapsed = Time.now - start
|
14
|
+
assert elapsed > 2
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_overall_timeout_on_dead_transfer
|
18
|
+
curl = Curl::Easy.new(wait_url(2))
|
19
|
+
curl.timeout = 1
|
20
|
+
assert_raise(Curl::Err::TimeoutError) do
|
21
|
+
curl.http_get
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_clearing_timeout
|
26
|
+
curl = Curl::Easy.new(wait_url(2))
|
27
|
+
curl.timeout = 1
|
28
|
+
curl.timeout = nil
|
29
|
+
start = Time.now
|
30
|
+
assert_equal true, curl.http_get
|
31
|
+
elapsed = Time.now - start
|
32
|
+
assert elapsed > 2
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_overall_timeout_on_slow_transfer
|
36
|
+
curl = Curl::Easy.new(serve_url(100, 2, 3))
|
37
|
+
curl.timeout = 1
|
38
|
+
# transfer is aborted despite data being exchanged
|
39
|
+
assert_raise(Curl::Err::TimeoutError) do
|
40
|
+
curl.http_get
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_low_speed_time_on_slow_transfer
|
45
|
+
curl = Curl::Easy.new(serve_url(100, 1, 3))
|
46
|
+
curl.low_speed_time = 2
|
47
|
+
# use default low_speed_limit of 1
|
48
|
+
assert true, curl.http_get
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_low_speed_time_on_very_slow_transfer
|
52
|
+
# send data slower than required
|
53
|
+
curl = Curl::Easy.new(serve_url(10, 2, 3))
|
54
|
+
curl.low_speed_time = 1
|
55
|
+
# XXX for some reason this test fails if low speed limit is not specified
|
56
|
+
curl.low_speed_limit = 1
|
57
|
+
# use default low_speed_limit of 1
|
58
|
+
assert_raise(Curl::Err::TimeoutError) do
|
59
|
+
curl.http_get
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_low_speed_limit_on_slow_transfer
|
64
|
+
curl = Curl::Easy.new(serve_url(10, 1, 3))
|
65
|
+
curl.low_speed_time = 2
|
66
|
+
curl.low_speed_limit = 1000
|
67
|
+
assert_raise(Curl::Err::TimeoutError) do
|
68
|
+
curl.http_get
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_clearing_low_speed_time
|
73
|
+
curl = Curl::Easy.new(serve_url(100, 2, 3))
|
74
|
+
curl.low_speed_time = 1
|
75
|
+
curl.low_speed_time = nil
|
76
|
+
assert_equal true, curl.http_get
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_clearing_low_speed_limit
|
80
|
+
curl = Curl::Easy.new(serve_url(10, 1, 3))
|
81
|
+
curl.low_speed_time = 2
|
82
|
+
curl.low_speed_limit = 1000
|
83
|
+
curl.low_speed_limit = nil
|
84
|
+
assert_equal true, curl.http_get
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def wait_url(time)
|
90
|
+
"#{server_base}/wait/#{time}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def serve_url(chunk_size, time, count)
|
94
|
+
"#{server_base}/serve/#{chunk_size}/every/#{time}/for/#{count}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def server_base
|
98
|
+
'http://127.0.0.1:9128'
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# This Sinatra application must be run with mongrel
|
2
|
+
# or possibly with unicorn for the serve action to work properly.
|
3
|
+
# See http://efreedom.com/Question/1-3669674/Streaming-Data-Sinatra-Rack-Application
|
4
|
+
|
5
|
+
require 'sinatra'
|
6
|
+
|
7
|
+
get '/wait/:time' do |time|
|
8
|
+
time = time.to_i
|
9
|
+
sleep(time)
|
10
|
+
"Slept #{time} at #{Time.now}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# http://efreedom.com/Question/1-3027435/Way-Flush-Html-Wire-Sinatra
|
14
|
+
class Streamer
|
15
|
+
def initialize(time, chunks)
|
16
|
+
@time = time
|
17
|
+
@chunks = chunks
|
18
|
+
end
|
19
|
+
|
20
|
+
def each
|
21
|
+
@chunks.each do |chunk|
|
22
|
+
sleep(@time)
|
23
|
+
yield chunk
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/serve/:chunk_size/every/:time/for/:count' do |chunk_size, time, count|
|
29
|
+
chunk_size, time, count = chunk_size.to_i, time.to_i, count.to_i
|
30
|
+
chunk = 'x' * chunk_size
|
31
|
+
chunks = [chunk] * count
|
32
|
+
Streamer.new(time, chunks)
|
33
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: curb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 0.7.
|
9
|
+
- 9
|
10
|
+
version: 0.7.9
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ross Bamford
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-12-22 00:00:00 -05:00
|
20
20
|
default_executable:
|
21
21
|
dependencies: []
|
22
22
|
|
@@ -66,6 +66,8 @@ files:
|
|
66
66
|
- tests/tc_curl_easy.rb
|
67
67
|
- tests/tc_curl_multi.rb
|
68
68
|
- tests/tc_curl_postfield.rb
|
69
|
+
- tests/timeout.rb
|
70
|
+
- tests/timeout_server.rb
|
69
71
|
- tests/unittests.rb
|
70
72
|
has_rdoc: true
|
71
73
|
homepage: http://curb.rubyforge.org/
|
@@ -120,4 +122,6 @@ test_files:
|
|
120
122
|
- tests/tc_curl_easy.rb
|
121
123
|
- tests/tc_curl_multi.rb
|
122
124
|
- tests/tc_curl_postfield.rb
|
125
|
+
- tests/timeout.rb
|
126
|
+
- tests/timeout_server.rb
|
123
127
|
- tests/unittests.rb
|