curb 0.7.8 → 0.7.9
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.
- 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
|