curb 0.9.11 → 1.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecd73a0ad8d98960321b8d523e25bad95ff25e8900e8acb187051ca40c7a1cba
4
- data.tar.gz: c154996d92946fe83fa12d695b073d5026e4a55e92efd177bfad021461f2b313
3
+ metadata.gz: b452d79ee28ee380d6741f39f9fbf826a33a46d473fa4444edb6a56a122af8f8
4
+ data.tar.gz: '094812699e5b64584d6dd02a17b16884ee696a20dbf324c05498a46509e39b16'
5
5
  SHA512:
6
- metadata.gz: 48854a4f0d77befe0e81b31a3d39e826b2ef65d58d0226b617a355ba8d063ab4998853a8709ae6d2e3c4629c09ff987099398a1c884b76d95dcf956d941fc660
7
- data.tar.gz: 3291675a74eb40f42d936bb65689da7d5f295e038ed019482d2cf115ba878232ce4e40aeb125d0fe786b5e82428320938ff3c75592a5c54a2d51fbe8589a0931
6
+ metadata.gz: 0df71c1a4eb7896f89da78a79ba9160d5606aa235b89544e08630c558a7cebb1220ec026f99f404915f52e15162cf2c988a66abe04932c3cd1104a1784bd3056
7
+ data.tar.gz: '08fba6b5b9a9404f839e4110b0d485e1595be36554345abb88a05482183204f4e0f13001a7731db75e89ca890bfa8ad19ccced63dfecd0e766f1b825ff5e9f1a'
data/README.markdown CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Curb (probably CUrl-RuBy or something) provides Ruby-language bindings for the
7
7
  libcurl(3), a fully-featured client-side URL transfer library.
8
- cURL and libcurl live at [http://curl.haxx.se/](http://curl.haxx.se/) .
8
+ cURL and libcurl live at [https://curl.se/libcurl/](https://curl.se/libcurl/) .
9
9
 
10
10
  Curb is a work-in-progress, and currently only supports libcurl's `easy` and `multi` modes.
11
11
 
@@ -14,9 +14,31 @@ Curb is a work-in-progress, and currently only supports libcurl's `easy` and `mu
14
14
  Curb is copyright (c)2006 Ross Bamford, and released under the terms of the
15
15
  Ruby license. See the LICENSE file for the gory details.
16
16
 
17
+ ## Easy mode
18
+
19
+ Get stuff
20
+ ```
21
+ res = Curl.get("https://www.google.com/")
22
+ puts res.response_code
23
+ puts res.header_str
24
+ puts res.body
25
+ ```
26
+
27
+ Post stuff
28
+ ```
29
+ res = Curl.post("https://your-server.com/endpoint", {post: "this"}.to_json) {|http|
30
+ http.headers["Content-Type"] = "application/json"
31
+ }
32
+ puts res.response_code
33
+ puts res.header_str
34
+ puts res.body
35
+ ```
36
+
37
+
38
+
17
39
  ## You will need
18
40
 
19
- * A working Ruby installation (`1.8.7+` will work but `2.1+` preferred)
41
+ * A working Ruby installation (`2.0.0+` will work but `2.1+` preferred) (it's possible it still works with 1.8.7 but you'd have to tell me if not...)
20
42
  * A working libcurl development installation
21
43
  (Ideally one of the versions listed in the compatibility chart below that maps to your `curb` version)
22
44
  * A sane build environment (e.g. gcc, make)
@@ -29,7 +51,8 @@ tested and reported to work across a variety of platforms / rubies)
29
51
 
30
52
  | Gem Version | Release Date | libcurl versions |
31
53
  | ----------- | ----------- | ---------------- |
32
- | 0.9.8 | Jan 2019 | 7.58 - 7.63 |
54
+ | 1.0.0 | Jan 2022 | 7.58 - 7.81 |
55
+ | 0.9.8 | Jan 2019 | 7.58 - 7.81 |
33
56
  | 0.9.7 | Nov 2018 | 7.56 - 7.60 |
34
57
  | 0.9.6 | May 2018 | 7.51 - 7.59 |
35
58
  | 0.9.5 | May 2018 | 7.51 - 7.59 |
@@ -43,7 +66,7 @@ tested and reported to work across a variety of platforms / rubies)
43
66
  $ gem install curb
44
67
 
45
68
  On Windows, make sure you're using the [DevKit](http://rubyinstaller.org/downloads/) and
46
- the [development version of libcurl](http://curl.haxx.se/gknw.net/7.39.0/dist-w32/curl-7.39.0-devel-mingw32.zip). Unzip, then run this in your command
69
+ the [development version of libcurl](http://curl.se/gknw.net/7.39.0/dist-w32/curl-7.39.0-devel-mingw32.zip). Unzip, then run this in your command
47
70
  line (alter paths to your curl location, but remember to use forward slashes):
48
71
 
49
72
  gem install curb --platform=ruby -- --with-curl-lib=C:/curl-7.39.0-devel-mingw32/lib --with-curl-include=C:/curl-7.39.0-devel-mingw32/include
@@ -67,6 +90,12 @@ expand on the above instructions, please report the issue at http://github.com/t
67
90
 
68
91
  On Ubuntu, the dependencies can be satisfied by installing the following packages:
69
92
 
93
+ 18.04 and onwards
94
+
95
+ $ sudo apt-get install libcurl4 libcurl3-gnutls libcurl4-openssl-dev
96
+
97
+ < 18.04
98
+
70
99
  $ sudo apt-get install libcurl3 libcurl3-gnutls libcurl4-openssl-dev
71
100
 
72
101
  On RedHat:
data/Rakefile CHANGED
@@ -57,22 +57,20 @@ MAKEOPTS = ENV['MAKE_OPTS'] || ''
57
57
  CURB_SO = "ext/curb_core.#{(defined?(RbConfig) ? RbConfig : Config)::MAKEFILE_CONFIG['DLEXT']}"
58
58
 
59
59
  file 'ext/Makefile' => 'ext/extconf.rb' do
60
- Dir.chdir('ext') do
61
- shell('ruby', 'extconf.rb', ENV['EXTCONF_OPTS'].to_s, live_stdout: STDOUT)
62
- end
60
+ shell(['ruby', 'extconf.rb', ENV['EXTCONF_OPTS'].to_s],
61
+ { :live_stdout => STDOUT , :cwd => "#{Dir.pwd}/ext" }
62
+ ).error!
63
63
  end
64
64
 
65
65
  def make(target = '')
66
- Dir.chdir('ext') do
67
- pid = system("#{MAKECMD} #{MAKEOPTS} #{target}")
68
- $?.exitstatus
69
- end
66
+ shell(["#{MAKECMD}", "#{MAKEOPTS}", "#{target}"].reject(&:empty?),
67
+ { :live_stdout => STDOUT, :cwd => "#{Dir.pwd}/ext" }
68
+ ).error!
70
69
  end
71
70
 
72
71
  # Let make handle dependencies between c/o/so - we'll just run it.
73
72
  file CURB_SO => (['ext/Makefile'] + Dir['ext/*.c'] + Dir['ext/*.h']) do
74
- m = make
75
- fail "Make failed (status #{m})" unless m == 0
73
+ make
76
74
  end
77
75
 
78
76
  desc "Compile the shared object"
@@ -80,8 +78,7 @@ task :compile => [CURB_SO]
80
78
 
81
79
  desc "Install to your site_ruby directory"
82
80
  task :install do
83
- m = make 'install'
84
- fail "Make install failed (status #{m})" unless m == 0
81
+ make 'install'
85
82
  end
86
83
 
87
84
  # Test Tasks ---------------------------------------------------------
data/ext/curb.c CHANGED
@@ -235,12 +235,16 @@ static VALUE ruby_curl_http2_q(VALUE mod) {
235
235
  #endif
236
236
  }
237
237
 
238
+ static void finalize_curb_core(VALUE data) {
239
+ curl_global_cleanup();
240
+ }
241
+
238
242
  void Init_curb_core() {
239
- // TODO we need to call curl_global_cleanup at exit!
240
243
  curl_version_info_data *ver;
241
244
  VALUE curlver, curllongver, curlvernum;
242
245
 
243
246
  curl_global_init(CURL_GLOBAL_ALL);
247
+ rb_set_end_proc(finalize_curb_core, Qnil);
244
248
  ver = curl_version_info(CURLVERSION_NOW);
245
249
 
246
250
  mCurl = rb_define_module("Curl");
@@ -292,18 +296,26 @@ void Init_curb_core() {
292
296
  #endif
293
297
 
294
298
  #ifdef CURL_VERSION_SSL
295
- rb_define_const(mCurl, "CURL_SSLVERSION_DEFAULT", LONG2NUM(CURL_SSLVERSION_DEFAULT));
299
+ rb_define_const(mCurl, "CURL_SSLVERSION_DEFAULT", LONG2NUM(CURL_SSLVERSION_DEFAULT));
300
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_DEFAULT", LONG2NUM(CURL_SSLVERSION_MAX_DEFAULT));
296
301
  rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1", LONG2NUM(CURL_SSLVERSION_TLSv1));
297
302
  rb_define_const(mCurl, "CURL_SSLVERSION_SSLv2", LONG2NUM(CURL_SSLVERSION_SSLv2));
298
303
  rb_define_const(mCurl, "CURL_SSLVERSION_SSLv3", LONG2NUM(CURL_SSLVERSION_SSLv3));
299
304
  #if HAVE_CURL_SSLVERSION_TLSV1_0
300
- rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_0", LONG2NUM(CURL_SSLVERSION_TLSv1_0));
305
+ rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_0", LONG2NUM(CURL_SSLVERSION_TLSv1_0));
306
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_0", LONG2NUM(CURL_SSLVERSION_MAX_TLSv1_0));
301
307
  #endif
302
308
  #if HAVE_CURL_SSLVERSION_TLSV1_1
303
- rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_1", LONG2NUM(CURL_SSLVERSION_TLSv1_1));
309
+ rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_1", LONG2NUM(CURL_SSLVERSION_TLSv1_1));
310
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_1", LONG2NUM(CURL_SSLVERSION_MAX_TLSv1_1));
304
311
  #endif
305
312
  #if HAVE_CURL_SSLVERSION_TLSV1_2
306
- rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_2", LONG2NUM(CURL_SSLVERSION_TLSv1_2));
313
+ rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_2", LONG2NUM(CURL_SSLVERSION_TLSv1_2));
314
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_2", LONG2NUM(CURL_SSLVERSION_MAX_TLSv1_2));
315
+ #endif
316
+ #if HAVE_CURL_SSLVERSION_TLSV1_3
317
+ rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_3", LONG2NUM(CURL_SSLVERSION_TLSv1_3));
318
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_3", LONG2NUM(CURL_SSLVERSION_MAX_TLSv1_3));
307
319
  #endif
308
320
 
309
321
  rb_define_const(mCurl, "CURL_USESSL_CONTROL", LONG2NUM(CURB_FTPSSL_CONTROL));
@@ -316,13 +328,20 @@ void Init_curb_core() {
316
328
  rb_define_const(mCurl, "CURL_SSLVERSION_SSLv2", LONG2NUM(-1));
317
329
  rb_define_const(mCurl, "CURL_SSLVERSION_SSLv3", LONG2NUM(-1));
318
330
  #if HAVE_CURL_SSLVERSION_TLSv1_0
319
- rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_0", LONG2NUM(-1));
331
+ rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_0", LONG2NUM(-1));
332
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_0", LONG2NUM(-1));
320
333
  #endif
321
334
  #if HAVE_CURL_SSLVERSION_TLSv1_1
322
- rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_1", LONG2NUM(-1));
335
+ rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_1", LONG2NUM(-1));
336
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_1", LONG2NUM(-1));
323
337
  #endif
324
338
  #if HAVE_CURL_SSLVERSION_TLSv1_2
325
- rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_2", LONG2NUM(-1));
339
+ rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_2", LONG2NUM(-1));
340
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_2", LONG2NUM(-1));
341
+ #endif
342
+ #if HAVE_CURL_SSLVERSION_TLSv1_3
343
+ rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_3", LONG2NUM(-1));
344
+ rb_define_const(mCurl, "CURL_SSLVERSION_MAX_TLSv1_3", LONG2NUM(-1));
326
345
  #endif
327
346
 
328
347
  rb_define_const(mCurl, "CURL_USESSL_CONTROL", LONG2NUM(-1));
@@ -925,12 +944,19 @@ void Init_curb_core() {
925
944
  #endif
926
945
  #if HAVE_CURL_SSLVERSION_TLSv1_0
927
946
  CURB_DEFINE(CURL_SSLVERSION_TLSv1_0);
947
+ CURB_DEFINE(CURL_SSLVERSION_MAX_TLSv1_0);
928
948
  #endif
929
949
  #if HAVE_CURL_SSLVERSION_TLSv1_1
930
950
  CURB_DEFINE(CURL_SSLVERSION_TLSv1_1);
951
+ CURB_DEFINE(CURL_SSLVERSION_MAX_TLSv1_1);
931
952
  #endif
932
953
  #if HAVE_CURL_SSLVERSION_TLSv1_2
933
954
  CURB_DEFINE(CURL_SSLVERSION_TLSv1_2);
955
+ CURB_DEFINE(CURL_SSLVERSION_MAX_TLSv1_2);
956
+ #endif
957
+ #if HAVE_CURL_SSLVERSION_TLSv1_3
958
+ CURB_DEFINE(CURL_SSLVERSION_TLSv1_3);
959
+ CURB_DEFINE(CURL_SSLVERSION_MAX_TLSv1_3);
934
960
  #endif
935
961
  #if HAVE_CURLOPT_SSL_VERIFYPEER
936
962
  CURB_DEFINE(CURLOPT_SSL_VERIFYPEER);
@@ -1050,6 +1076,122 @@ void Init_curb_core() {
1050
1076
  CURB_DEFINE(CURLOPT_HAPROXYPROTOCOL);
1051
1077
  #endif
1052
1078
 
1079
+ #if HAVE_CURLPROTO_RTMPTE
1080
+ CURB_DEFINE(CURLPROTO_RTMPTE);
1081
+ #endif
1082
+
1083
+ #if HAVE_CURLPROTO_RTMPTS
1084
+ CURB_DEFINE(CURLPROTO_RTMPTS);
1085
+ #endif
1086
+
1087
+ #if HAVE_CURLPROTO_SMBS
1088
+ CURB_DEFINE(CURLPROTO_SMBS);
1089
+ #endif
1090
+
1091
+ #if HAVE_CURLPROTO_LDAP
1092
+ CURB_DEFINE(CURLPROTO_LDAP);
1093
+ #endif
1094
+
1095
+ #if HAVE_CURLPROTO_FTP
1096
+ CURB_DEFINE(CURLPROTO_FTP);
1097
+ #endif
1098
+
1099
+ #if HAVE_CURLPROTO_SMTPS
1100
+ CURB_DEFINE(CURLPROTO_SMTPS);
1101
+ #endif
1102
+
1103
+ #if HAVE_CURLPROTO_HTTP
1104
+ CURB_DEFINE(CURLPROTO_HTTP);
1105
+ #endif
1106
+
1107
+ #if HAVE_CURLPROTO_SMTP
1108
+ CURB_DEFINE(CURLPROTO_SMTP);
1109
+ #endif
1110
+
1111
+ #if HAVE_CURLPROTO_TFTP
1112
+ CURB_DEFINE(CURLPROTO_TFTP);
1113
+ #endif
1114
+
1115
+ #if HAVE_CURLPROTO_LDAPS
1116
+ CURB_DEFINE(CURLPROTO_LDAPS);
1117
+ #endif
1118
+
1119
+ #if HAVE_CURLPROTO_IMAPS
1120
+ CURB_DEFINE(CURLPROTO_IMAPS);
1121
+ #endif
1122
+
1123
+ #if HAVE_CURLPROTO_SCP
1124
+ CURB_DEFINE(CURLPROTO_SCP);
1125
+ #endif
1126
+
1127
+ #if HAVE_CURLPROTO_SFTP
1128
+ CURB_DEFINE(CURLPROTO_SFTP);
1129
+ #endif
1130
+
1131
+ #if HAVE_CURLPROTO_TELNET
1132
+ CURB_DEFINE(CURLPROTO_TELNET);
1133
+ #endif
1134
+
1135
+ #if HAVE_CURLPROTO_FILE
1136
+ CURB_DEFINE(CURLPROTO_FILE);
1137
+ #endif
1138
+
1139
+ #if HAVE_CURLPROTO_FTPS
1140
+ CURB_DEFINE(CURLPROTO_FTPS);
1141
+ #endif
1142
+
1143
+ #if HAVE_CURLPROTO_HTTPS
1144
+ CURB_DEFINE(CURLPROTO_HTTPS);
1145
+ #endif
1146
+
1147
+ #if HAVE_CURLPROTO_IMAP
1148
+ CURB_DEFINE(CURLPROTO_IMAP);
1149
+ #endif
1150
+
1151
+ #if HAVE_CURLPROTO_POP3
1152
+ CURB_DEFINE(CURLPROTO_POP3);
1153
+ #endif
1154
+
1155
+ #if HAVE_CURLPROTO_GOPHER
1156
+ CURB_DEFINE(CURLPROTO_GOPHER);
1157
+ #endif
1158
+
1159
+ #if HAVE_CURLPROTO_DICT
1160
+ CURB_DEFINE(CURLPROTO_DICT);
1161
+ #endif
1162
+
1163
+ #if HAVE_CURLPROTO_SMB
1164
+ CURB_DEFINE(CURLPROTO_SMB);
1165
+ #endif
1166
+
1167
+ #if HAVE_CURLPROTO_RTMP
1168
+ CURB_DEFINE(CURLPROTO_RTMP);
1169
+ #endif
1170
+
1171
+ #if HAVE_CURLPROTO_ALL
1172
+ CURB_DEFINE(CURLPROTO_ALL);
1173
+ #endif
1174
+
1175
+ #if HAVE_CURLPROTO_RTMPE
1176
+ CURB_DEFINE(CURLPROTO_RTMPE);
1177
+ #endif
1178
+
1179
+ #if HAVE_CURLPROTO_RTMPS
1180
+ CURB_DEFINE(CURLPROTO_RTMPS);
1181
+ #endif
1182
+
1183
+ #if HAVE_CURLPROTO_RTMPT
1184
+ CURB_DEFINE(CURLPROTO_RTMPT);
1185
+ #endif
1186
+
1187
+ #if HAVE_CURLPROTO_POP3S
1188
+ CURB_DEFINE(CURLPROTO_POP3S);
1189
+ #endif
1190
+
1191
+ #if HAVE_CURLPROTO_RTSP
1192
+ CURB_DEFINE(CURLPROTO_RTSP);
1193
+ #endif
1194
+
1053
1195
  #if LIBCURL_VERSION_NUM >= 0x072B00 /* 7.43.0 */
1054
1196
  CURB_DEFINE(CURLPIPE_NOTHING);
1055
1197
  CURB_DEFINE(CURLPIPE_HTTP1);
data/ext/curb.h CHANGED
@@ -28,11 +28,11 @@
28
28
  #include "curb_macros.h"
29
29
 
30
30
  // These should be managed from the Rake 'release' task.
31
- #define CURB_VERSION "0.9.11"
32
- #define CURB_VER_NUM 9011
33
- #define CURB_VER_MAJ 0
34
- #define CURB_VER_MIN 9
35
- #define CURB_VER_MIC 11
31
+ #define CURB_VERSION "1.0.1"
32
+ #define CURB_VER_NUM 1001
33
+ #define CURB_VER_MAJ 1
34
+ #define CURB_VER_MIN 0
35
+ #define CURB_VER_MIC 1
36
36
  #define CURB_VER_PATCH 0
37
37
 
38
38
 
@@ -49,6 +49,11 @@
49
49
  #define RHASH_SIZE(hash) RHASH(hash)->tbl->num_entries
50
50
  #endif
51
51
 
52
+ // ruby 1.8 does not provide the macro
53
+ #ifndef DBL2NUM
54
+ #define DBL2NUM(dbl) rb_float_new(dbl)
55
+ #endif
56
+
52
57
  extern VALUE mCurl;
53
58
 
54
59
  extern void Init_curb_core();
data/ext/curb_easy.c CHANGED
@@ -268,6 +268,8 @@ void curl_easy_free(ruby_curl_easy *rbce) {
268
268
  static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
269
269
  rbce->opts = rb_hash_new();
270
270
 
271
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
272
+
271
273
  rbce->curl_headers = NULL;
272
274
  rbce->curl_proxy_headers = NULL;
273
275
  rbce->curl_ftp_commands = NULL;
@@ -327,9 +329,9 @@ static VALUE ruby_curl_easy_allocate(VALUE klass) {
327
329
 
328
330
  /*
329
331
  * call-seq:
330
- * Curl::Easy.new => #&lt;Curl::Easy...&gt;
331
- * Curl::Easy.new(url = nil) => #&lt;Curl::Easy...&gt;
332
- * Curl::Easy.new(url = nil) { |self| ... } => #&lt;Curl::Easy...&gt;
332
+ * Curl::Easy.new => #<Curl::Easy...>
333
+ * Curl::Easy.new(url = nil) => #<Curl::Easy...>
334
+ * Curl::Easy.new(url = nil) { |self| ... } => #<Curl::Easy...>
333
335
  *
334
336
  * Initialize a new Curl::Easy instance, optionally supplying the URL.
335
337
  * The block form allows further configuration to be supplied before
@@ -355,8 +357,9 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
355
357
 
356
358
  ruby_curl_easy_zero(rbce);
357
359
 
358
- rb_easy_set("url", url);
360
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
359
361
 
362
+ rb_easy_set("url", url);
360
363
 
361
364
  /* set the pointer to the curl handle */
362
365
  ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
@@ -373,8 +376,8 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
373
376
 
374
377
  /*
375
378
  * call-seq:
376
- * easy.clone => #&lt;easy clone&gt;
377
- * easy.dup => #&lt;easy clone&gt;
379
+ * easy.clone => <easy clone>
380
+ * easy.dup => <easy clone>
378
381
  *
379
382
  * Clone this Curl::Easy instance, creating a new instance.
380
383
  * This method duplicates the underlying CURL* handle.
@@ -392,6 +395,8 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
392
395
  newrbce->curl_ftp_commands = NULL;
393
396
  newrbce->curl_resolve = NULL;
394
397
 
398
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
399
+
395
400
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
396
401
  }
397
402
 
@@ -461,7 +466,9 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
461
466
  curl_easy_reset(rbce->curl);
462
467
  ruby_curl_easy_zero(rbce);
463
468
 
464
- /* rest clobbers the private setting, so reset it to self */
469
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
470
+
471
+ /* reset clobbers the private setting, so reset it to self */
465
472
  ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
466
473
  if (ecode != CURLE_OK) {
467
474
  raise_curl_easy_error_exception(ecode);
@@ -950,7 +957,7 @@ static VALUE ruby_curl_easy_ftp_commands_set(VALUE self, VALUE ftp_commands) {
950
957
  }
951
958
 
952
959
  /*
953
- * call-seq
960
+ * call-seq:
954
961
  * easy.ftp_commands => array or nil
955
962
  */
956
963
  static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) {
@@ -969,7 +976,7 @@ static VALUE ruby_curl_easy_resolve_set(VALUE self, VALUE resolve) {
969
976
  }
970
977
 
971
978
  /*
972
- * call-seq
979
+ * call-seq:
973
980
  * easy.resolve => array or nil
974
981
  */
975
982
  static VALUE ruby_curl_easy_resolve_get(VALUE self) {
@@ -1539,6 +1546,7 @@ static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
1539
1546
  * Curl::CURL_SSLVERSION_TLSv1_0
1540
1547
  * Curl::CURL_SSLVERSION_TLSv1_1
1541
1548
  * Curl::CURL_SSLVERSION_TLSv1_2
1549
+ * Curl::CURL_SSLVERSION_TLSv1_3
1542
1550
  */
1543
1551
  static VALUE ruby_curl_easy_ssl_version_set(VALUE self, VALUE ssl_version) {
1544
1552
  CURB_IMMED_SETTER(ruby_curl_easy, ssl_version, -1);
@@ -1587,7 +1595,7 @@ static VALUE ruby_curl_easy_ftp_filemethod_set(VALUE self, VALUE ftp_filemethod)
1587
1595
  }
1588
1596
 
1589
1597
  /*
1590
- * call-seq
1598
+ * call-seq:
1591
1599
  * easy.ftp_filemethod => fixnum
1592
1600
  *
1593
1601
  * Get the configuration for how libcurl will reach files on the server.
@@ -1959,7 +1967,7 @@ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) {
1959
1967
 
1960
1968
  /*
1961
1969
  * call-seq:
1962
- * easy.on_body { |body_data| ... } => &lt;old handler&gt;
1970
+ * easy.on_body { |body_data| ... } => <old handler>
1963
1971
  *
1964
1972
  * Assign or remove the +on_body+ handler for this Curl::Easy instance.
1965
1973
  * To remove a previously-supplied handler, call this method with no
@@ -1978,7 +1986,7 @@ static VALUE ruby_curl_easy_on_body_set(int argc, VALUE *argv, VALUE self) {
1978
1986
 
1979
1987
  /*
1980
1988
  * call-seq:
1981
- * easy.on_success { |easy| ... } => &lt;old handler&gt;
1989
+ * easy.on_success { |easy| ... } => <old handler>
1982
1990
  *
1983
1991
  * Assign or remove the +on_success+ handler for this Curl::Easy instance.
1984
1992
  * To remove a previously-supplied handler, call this method with no
@@ -1993,7 +2001,7 @@ static VALUE ruby_curl_easy_on_success_set(int argc, VALUE *argv, VALUE self) {
1993
2001
 
1994
2002
  /*
1995
2003
  * call-seq:
1996
- * easy.on_failure {|easy,code| ... } => &lt;old handler&gt;
2004
+ * easy.on_failure {|easy,code| ... } => <old handler>
1997
2005
  *
1998
2006
  * Assign or remove the +on_failure+ handler for this Curl::Easy instance.
1999
2007
  * To remove a previously-supplied handler, call this method with no
@@ -2008,7 +2016,7 @@ static VALUE ruby_curl_easy_on_failure_set(int argc, VALUE *argv, VALUE self) {
2008
2016
 
2009
2017
  /*
2010
2018
  * call-seq:
2011
- * easy.on_missing {|easy,code| ... } => &lt;old handler;&gt;
2019
+ * easy.on_missing {|easy,code| ... } => <old handler;>
2012
2020
  *
2013
2021
  * Assign or remove the on_missing handler for this Curl::Easy instance.
2014
2022
  * To remove a previously-supplied handler, call this method with no attached
@@ -2023,7 +2031,7 @@ static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
2023
2031
 
2024
2032
  /*
2025
2033
  * call-seq:
2026
- * easy.on_redirect {|easy,code| ... } => &lt;old handler;&gt;
2034
+ * easy.on_redirect {|easy,code| ... } => <old handler;>
2027
2035
  *
2028
2036
  * Assign or remove the on_redirect handler for this Curl::Easy instance.
2029
2037
  * To remove a previously-supplied handler, call this method with no attached
@@ -2038,7 +2046,7 @@ static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) {
2038
2046
 
2039
2047
  /*
2040
2048
  * call-seq:
2041
- * easy.on_complete {|easy| ... } => &lt;old handler&gt;
2049
+ * easy.on_complete {|easy| ... } => <old handler>
2042
2050
  *
2043
2051
  * Assign or remove the +on_complete+ handler for this Curl::Easy instance.
2044
2052
  * To remove a previously-supplied handler, call this method with no
@@ -2052,7 +2060,7 @@ static VALUE ruby_curl_easy_on_complete_set(int argc, VALUE *argv, VALUE self) {
2052
2060
 
2053
2061
  /*
2054
2062
  * call-seq:
2055
- * easy.on_header { |header_data| ... } => &lt;old handler&gt;
2063
+ * easy.on_header { |header_data| ... } => <old handler>
2056
2064
  *
2057
2065
  * Assign or remove the +on_header+ handler for this Curl::Easy instance.
2058
2066
  * To remove a previously-supplied handler, call this method with no
@@ -2068,7 +2076,7 @@ static VALUE ruby_curl_easy_on_header_set(int argc, VALUE *argv, VALUE self) {
2068
2076
 
2069
2077
  /*
2070
2078
  * call-seq:
2071
- * easy.on_progress { |dl_total, dl_now, ul_total, ul_now| ... } => &lt;old handler&gt;
2079
+ * easy.on_progress { |dl_total, dl_now, ul_total, ul_now| ... } => <old handler>
2072
2080
  *
2073
2081
  * Assign or remove the +on_progress+ handler for this Curl::Easy instance.
2074
2082
  * To remove a previously-supplied handler, call this method with no
@@ -2089,7 +2097,7 @@ static VALUE ruby_curl_easy_on_progress_set(int argc, VALUE *argv, VALUE self) {
2089
2097
 
2090
2098
  /*
2091
2099
  * call-seq:
2092
- * easy.on_debug { |type, data| ... } => &lt;old handler&gt;
2100
+ * easy.on_debug { |type, data| ... } => <old handler>
2093
2101
  *
2094
2102
  * Assign or remove the +on_debug+ handler for this Curl::Easy instance.
2095
2103
  * To remove a previously-supplied handler, call this method with no
@@ -2128,11 +2136,14 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
2128
2136
 
2129
2137
  name = rb_obj_as_string(rb_ary_entry(header, 0));
2130
2138
  value = rb_obj_as_string(rb_ary_entry(header, 1));
2131
-
2132
- // This is a bit inefficient, but we don't want to be modifying
2133
- // the actual values in the original hash.
2134
- header_str = rb_str_plus(name, rb_str_new2(": "));
2135
- header_str = rb_str_plus(header_str, value);
2139
+ if (rb_str_strlen(value) == 0) { // removing the header e.g. Accept: with nothing trailing should remove it see: https://curl.se/libcurl/c/CURLOPT_HTTPHEADER.html
2140
+ header_str = rb_str_plus(name, rb_str_new2(":"));
2141
+ } else {
2142
+ // This is a bit inefficient, but we don't want to be modifying
2143
+ // the actual values in the original hash.
2144
+ header_str = rb_str_plus(name, rb_str_new2(": "));
2145
+ header_str = rb_str_plus(header_str, value);
2146
+ }
2136
2147
  } else {
2137
2148
  header_str = rb_obj_as_string(header);
2138
2149
  }
@@ -2607,6 +2618,8 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
2607
2618
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2608
2619
  curl = rbce->curl;
2609
2620
 
2621
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2622
+
2610
2623
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb);
2611
2624
 
2612
2625
  retval = rb_funcall(self, rb_intern("perform"), 0);
@@ -2672,6 +2685,8 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2672
2685
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2673
2686
  curl = rbce->curl;
2674
2687
 
2688
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2689
+
2675
2690
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2676
2691
 
2677
2692
  if (rbce->multipart_form_post) {
@@ -2743,6 +2758,8 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2743
2758
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2744
2759
  curl = rbce->curl;
2745
2760
 
2761
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2762
+
2746
2763
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2747
2764
  ruby_curl_easy_put_data_set(self, data);
2748
2765
 
@@ -2761,6 +2778,10 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2761
2778
  * your own body handler, this string will be empty.
2762
2779
  */
2763
2780
  static VALUE ruby_curl_easy_body_str_get(VALUE self) {
2781
+ /*
2782
+ TODO: can we force_encoding on the return here if we see charset=utf-8 in the content-type header?
2783
+ Content-Type: application/json; charset=utf-8
2784
+ */
2764
2785
  CURB_OBJECT_HGETTER(ruby_curl_easy, body_data);
2765
2786
  }
2766
2787
 
@@ -2955,7 +2976,7 @@ static VALUE ruby_curl_easy_connect_time_get(VALUE self) {
2955
2976
  * Retrieve the time, in seconds, it took from the start until the SSL/SSH
2956
2977
  * connect/handshake to the remote host was completed. This time is most often
2957
2978
  * very near to the pre transfer time, except for cases such as HTTP
2958
- * pippelining where the pretransfer time can be delayed due to waits in line
2979
+ * pipelining where the pretransfer time can be delayed due to waits in line
2959
2980
  * for the pipeline and more.
2960
2981
  */
2961
2982
  #if defined(HAVE_CURLINFO_APPCONNECT_TIME)
@@ -3448,6 +3469,21 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3448
3469
  return LONG2NUM(rbce->last_result);
3449
3470
  }
3450
3471
 
3472
+ /*
3473
+ * call-seq:
3474
+ * easy.last_error => "Error details" or nil
3475
+ */
3476
+ static VALUE ruby_curl_easy_last_error(VALUE self) {
3477
+ ruby_curl_easy *rbce;
3478
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
3479
+
3480
+ if (rbce->err_buf[0]) { // curl returns NULL or empty string if none
3481
+ return rb_str_new2(rbce->err_buf);
3482
+ } else {
3483
+ return Qnil;
3484
+ }
3485
+ }
3486
+
3451
3487
  /*
3452
3488
  * call-seq:
3453
3489
  * easy.setopt Fixnum, value => value
@@ -3546,6 +3582,9 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3546
3582
  case CURLOPT_TCP_NODELAY: {
3547
3583
  curl_easy_setopt(rbce->curl, CURLOPT_TCP_NODELAY, NUM2LONG(val));
3548
3584
  } break;
3585
+ case CURLOPT_RANGE: {
3586
+ curl_easy_setopt(rbce->curl, CURLOPT_RANGE, StringValueCStr(val));
3587
+ } break;
3549
3588
  case CURLOPT_RESUME_FROM: {
3550
3589
  curl_easy_setopt(rbce->curl, CURLOPT_RESUME_FROM, NUM2LONG(val));
3551
3590
  } break;
@@ -3606,6 +3645,15 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3606
3645
  GetOpenFile(val, open_f_ptr);
3607
3646
  curl_easy_setopt(rbce->curl, CURLOPT_STDERR, rb_io_stdio_file(open_f_ptr));
3608
3647
  break;
3648
+ case CURLOPT_PROTOCOLS:
3649
+ case CURLOPT_REDIR_PROTOCOLS:
3650
+ curl_easy_setopt(rbce->curl, option, NUM2LONG(val));
3651
+ break;
3652
+ #if HAVE_CURLOPT_SSL_SESSIONID_CACHE
3653
+ case CURLOPT_SSL_SESSIONID_CACHE:
3654
+ curl_easy_setopt(rbce->curl, CURLOPT_SSL_SESSIONID_CACHE, NUM2LONG(val));
3655
+ break;
3656
+ #endif
3609
3657
  default:
3610
3658
  rb_raise(rb_eTypeError, "Curb unsupported option");
3611
3659
  }
@@ -3913,6 +3961,7 @@ void init_curb_easy() {
3913
3961
  rb_define_method(cCurlEasy, "multi", ruby_curl_easy_multi_get, 0);
3914
3962
  rb_define_method(cCurlEasy, "multi=", ruby_curl_easy_multi_set, 1);
3915
3963
  rb_define_method(cCurlEasy, "last_result", ruby_curl_easy_last_result, 0);
3964
+ rb_define_method(cCurlEasy, "last_error", ruby_curl_easy_last_error, 0);
3916
3965
 
3917
3966
  rb_define_method(cCurlEasy, "setopt", ruby_curl_easy_set_opt, 2);
3918
3967
  rb_define_method(cCurlEasy, "getinfo", ruby_curl_easy_get_opt, 1);
data/ext/curb_easy.h CHANGED
@@ -36,6 +36,9 @@ typedef struct {
36
36
  /* The handler */
37
37
  CURL *curl;
38
38
 
39
+ /* Buffer for error details from CURLOPT_ERRORBUFFER */
40
+ char err_buf[CURL_ERROR_SIZE];
41
+
39
42
  VALUE opts; /* rather then allocate everything we might need to store, allocate a Hash and only store objects we actually use... */
40
43
  VALUE multi; /* keep a multi handle alive for each easy handle not being used by a multi handle. This improves easy performance when not within a multi context */
41
44
 
data/ext/curb_multi.c CHANGED
@@ -64,7 +64,7 @@ static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
64
64
 
65
65
  /*
66
66
  * call-seq:
67
- * Curl::Multi.new => #&lt;Curl::Easy...&gt;
67
+ * Curl::Multi.new => #<Curl::Easy...>
68
68
  *
69
69
  * Create a new Curl::Multi instance
70
70
  */
@@ -132,7 +132,7 @@ VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
132
132
 
133
133
  /*
134
134
  * call-seq:
135
- * multi.requests => [#&lt;Curl::Easy...&gt;, ...]
135
+ * multi.requests => [#<Curl::Easy...>, ...]
136
136
  *
137
137
  * Returns an array containing all the active requests on this Curl::Multi object.
138
138
  */
@@ -300,10 +300,14 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
300
300
  rbce->callback_active = 1;
301
301
  val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
302
302
  rbce->callback_active = 0;
303
- //rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
304
303
  }
305
304
 
305
+ #ifdef HAVE_CURLINFO_RESPONSE_CODE
306
306
  curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
307
+ #else
308
+ // old libcurl
309
+ curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &response_code);
310
+ #endif
307
311
 
308
312
  if (result != 0) {
309
313
  if (!rb_easy_nil("failure_proc")) {
@@ -313,8 +317,7 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
313
317
  rbce->callback_active = 0;
314
318
  //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
315
319
  }
316
- }
317
- else if (!rb_easy_nil("success_proc") &&
320
+ } else if (!rb_easy_nil("success_proc") &&
318
321
  ((response_code >= 200 && response_code < 300) || response_code == 0)) {
319
322
  /* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
320
323
  callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
@@ -322,22 +325,19 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
322
325
  val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
323
326
  rbce->callback_active = 0;
324
327
  //rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
325
- }
326
- else if (!rb_easy_nil("redirect_proc") &&
328
+ } else if (!rb_easy_nil("redirect_proc") &&
327
329
  (response_code >= 300 && response_code < 400)) {
328
330
  rbce->callback_active = 1;
329
331
  callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
330
332
  rbce->callback_active = 0;
331
333
  val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
332
- }
333
- else if (!rb_easy_nil("missing_proc") &&
334
+ } else if (!rb_easy_nil("missing_proc") &&
334
335
  (response_code >= 400 && response_code < 500)) {
335
336
  rbce->callback_active = 1;
336
337
  callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
337
338
  rbce->callback_active = 0;
338
339
  val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
339
- }
340
- else if (!rb_easy_nil("failure_proc") &&
340
+ } else if (!rb_easy_nil("failure_proc") &&
341
341
  (response_code >= 500 && response_code <= 999)) {
342
342
  callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
343
343
  rbce->callback_active = 1;
data/ext/curb_postfield.c CHANGED
@@ -195,9 +195,9 @@ void curl_postfield_free(ruby_curl_postfield *rbcpf) {
195
195
 
196
196
  /*
197
197
  * call-seq:
198
- * Curl::PostField.content(name, content) => #&lt;Curl::PostField...&gt;
199
- * Curl::PostField.content(name, content, content_type = nil) => #&lt;Curl::PostField...&gt;
200
- * Curl::PostField.content(name, content_type = nil) { |field| ... } => #&lt;Curl::PostField...&gt;
198
+ * Curl::PostField.content(name, content) => #<Curl::PostField...>
199
+ * Curl::PostField.content(name, content, content_type = nil) => #<Curl::PostField...>
200
+ * Curl::PostField.content(name, content_type = nil) { |field| ... } => #<Curl::PostField...>
201
201
  *
202
202
  * Create a new Curl::PostField, supplying the field name, content,
203
203
  * and, optionally, Content-type (curl will attempt to determine this if
@@ -241,9 +241,9 @@ static VALUE ruby_curl_postfield_new_content(int argc, VALUE *argv, VALUE klass)
241
241
 
242
242
  /*
243
243
  * call-seq:
244
- * Curl::PostField.file(name, local_file_name) => #&lt;Curl::PostField...&gt;
245
- * Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) => #&lt;Curl::PostField...&gt;
246
- * Curl::PostField.file(name, remote_file_name) { |field| ... } => #&lt;Curl::PostField...&gt;
244
+ * Curl::PostField.file(name, local_file_name) => #<Curl::PostField...>
245
+ * Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) => #<Curl::PostField...>
246
+ * Curl::PostField.file(name, remote_file_name) { |field| ... } => #<Curl::PostField...>
247
247
  *
248
248
  * Create a new Curl::PostField for a file upload field, supplying the local filename
249
249
  * to read from, and optionally the remote filename (defaults to the local name).
@@ -399,7 +399,7 @@ static VALUE ruby_curl_postfield_remote_file_get(VALUE self) {
399
399
 
400
400
  /*
401
401
  * call-seq:
402
- * field.set_content_proc { |field| ... } => &lt;old proc&gt;
402
+ * field.set_content_proc { |field| ... } => <old proc>
403
403
  *
404
404
  * Set a content proc for this field. This proc will be called during the
405
405
  * perform to supply the content for this field, overriding any setting
data/ext/extconf.rb CHANGED
@@ -18,6 +18,8 @@ elsif !have_library('curl') or !have_header('curl/curl.h')
18
18
  fail <<-EOM
19
19
  Can't find libcurl or curl/curl.h
20
20
 
21
+ Make sure development libs (ie libcurl4-openssl-dev) are installed on the system.
22
+
21
23
  Try passing --with-curl-dir or --with-curl-lib and --with-curl-include
22
24
  options to extconf.
23
25
  EOM
@@ -341,6 +343,9 @@ have_constant :CURL_SSLVERSION_TLSv1_0
341
343
  have_constant :CURL_SSLVERSION_TLSv1_1
342
344
  have_constant :CURL_SSLVERSION_TLSv1_2
343
345
 
346
+ # Added in 7.52.0
347
+ have_constant :CURL_SSLVERSION_TLSv1_3
348
+
344
349
  have_constant "curlopt_ssl_verifypeer"
345
350
  have_constant "curlopt_cainfo"
346
351
  have_constant "curlopt_issuercert"
@@ -400,6 +405,37 @@ have_constant "curlopt_path_as_is"
400
405
  # added in 7.43.0
401
406
  have_constant "curlopt_pipewait"
402
407
 
408
+ # protocol constants
409
+ have_constant "curlproto_all"
410
+ have_constant "curlproto_dict"
411
+ have_constant "curlproto_file"
412
+ have_constant "curlproto_ftp"
413
+ have_constant "curlproto_ftps"
414
+ have_constant "curlproto_gopher"
415
+ have_constant "curlproto_http"
416
+ have_constant "curlproto_https"
417
+ have_constant "curlproto_imap"
418
+ have_constant "curlproto_imaps"
419
+ have_constant "curlproto_ldap"
420
+ have_constant "curlproto_ldaps"
421
+ have_constant "curlproto_pop3"
422
+ have_constant "curlproto_pop3s"
423
+ have_constant "curlproto_rtmp"
424
+ have_constant "curlproto_rtmpe"
425
+ have_constant "curlproto_rtmps"
426
+ have_constant "curlproto_rtmpt"
427
+ have_constant "curlproto_rtmpte"
428
+ have_constant "curlproto_rtmpts"
429
+ have_constant "curlproto_rtsp"
430
+ have_constant "curlproto_scp"
431
+ have_constant "curlproto_sftp"
432
+ have_constant "curlproto_smb"
433
+ have_constant "curlproto_smbs"
434
+ have_constant "curlproto_smtp"
435
+ have_constant "curlproto_smtps"
436
+ have_constant "curlproto_telnet"
437
+ have_constant "curlproto_tftp"
438
+
403
439
  if try_compile('int main() { return 0; }','-Wall')
404
440
  $CFLAGS << ' -Wall'
405
441
  end
data/lib/curl/easy.rb CHANGED
@@ -21,7 +21,7 @@ module Curl
21
21
  #
22
22
  def status
23
23
  # Matches the last HTTP Status - following the HTTP protocol specification 'Status-Line = HTTP-Version SP Status-Code SP (Opt:)Reason-Phrase CRLF'
24
- statuses = self.header_str.scan(/HTTP\/\d(\.\d)?\s(\d+\s.*)\r\n/).map{ |match| match[1] }
24
+ statuses = self.header_str.to_s.scan(/HTTP\/\d(\.\d)?\s(\d+\s.*)\r\n/).map {|match| match[1] }
25
25
  statuses.last.strip if statuses.length > 0
26
26
  end
27
27
 
@@ -75,8 +75,9 @@ module Curl
75
75
  end
76
76
 
77
77
  if self.last_result != 0 && self.on_failure.nil?
78
- error = Curl::Easy.error(self.last_result)
79
- raise error.first.new(self.head)
78
+ (err_class, err_summary) = Curl::Easy.error(self.last_result)
79
+ err_detail = self.last_error
80
+ raise err_class.new([err_summary, err_detail].compact.join(": "))
80
81
  end
81
82
 
82
83
  ret
@@ -320,7 +321,7 @@ module Curl
320
321
 
321
322
  #
322
323
  # call-seq:
323
- # Curl::Easy.perform(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
324
+ # Curl::Easy.perform(url) { |easy| ... } => #<Curl::Easy...>
324
325
  #
325
326
  # Convenience method that creates a new Curl::Easy instance with
326
327
  # the specified URL and calls the general +perform+ method, before returning
@@ -338,7 +339,7 @@ module Curl
338
339
 
339
340
  #
340
341
  # call-seq:
341
- # Curl::Easy.http_get(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
342
+ # Curl::Easy.http_get(url) { |easy| ... } => #<Curl::Easy...>
342
343
  #
343
344
  # Convenience method that creates a new Curl::Easy instance with
344
345
  # the specified URL and calls +http_get+, before returning the new instance.
@@ -355,7 +356,7 @@ module Curl
355
356
 
356
357
  #
357
358
  # call-seq:
358
- # Curl::Easy.http_head(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
359
+ # Curl::Easy.http_head(url) { |easy| ... } => #<Curl::Easy...>
359
360
  #
360
361
  # Convenience method that creates a new Curl::Easy instance with
361
362
  # the specified URL and calls +http_head+, before returning the new instance.
@@ -409,7 +410,7 @@ module Curl
409
410
 
410
411
  #
411
412
  # call-seq:
412
- # Curl::Easy.http_delete(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
413
+ # Curl::Easy.http_delete(url) { |easy| ... } => #<Curl::Easy...>
413
414
  #
414
415
  # Convenience method that creates a new Curl::Easy instance with
415
416
  # the specified URL and calls +http_delete+, before returning the new instance.
data/lib/curl.rb CHANGED
@@ -9,12 +9,20 @@ require 'cgi'
9
9
  module Curl
10
10
 
11
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
12
+ if Thread.current[:curb_curl_yielding]
13
+ handle = Curl::Easy.new # we can't reuse this
14
+ else
15
+ handle = Thread.current[:curb_curl] ||= Curl::Easy.new
16
+ handle.reset
17
+ end
14
18
  handle.url = url
15
19
  handle.post_body = post_body if post_body
16
20
  handle.put_data = put_data if put_data
17
- yield handle if block_given?
21
+ if block_given?
22
+ Thread.current[:curb_curl_yielding] = true
23
+ yield handle
24
+ Thread.current[:curb_curl_yielding] = false
25
+ end
18
26
  handle.http(verb)
19
27
  handle
20
28
  end
data/tests/helper.rb CHANGED
@@ -143,7 +143,6 @@ module TestServerMethods
143
143
  def server_setup(port=9129,servlet=TestServlet)
144
144
  @__port = port
145
145
  if (@server ||= nil).nil? and !File.exist?(locked_file)
146
-
147
146
  File.open(locked_file,'w') {|f| f << 'locked' }
148
147
  if TEST_SINGLE_THREADED
149
148
  rd, wr = IO.pipe
@@ -10,6 +10,14 @@ class TestCurbCurlEasy < Test::Unit::TestCase
10
10
  Curl.reset
11
11
  end
12
12
 
13
+ def test_nested_easy_methods
14
+ easy = Curl.get(TestServlet.url) {|http|
15
+ res = Curl.get(TestServlet.url + '/not_here')
16
+ assert_equal 404, res.response_code
17
+ }
18
+ assert_equal 200, easy.response_code
19
+ end
20
+
13
21
  def test_curlopt_stderr_with_file
14
22
  # does not work with Tempfile directly
15
23
  path = Tempfile.new('curb_test_curlopt_stderr').path
@@ -21,7 +29,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
21
29
  end
22
30
  output = File.read(path)
23
31
 
24
- assert_match('HTTP/1.1 200 OK ', output)
32
+ assert_match(/HTTP\/1\.1\ 200\ OK(?:\ )?/, output)
25
33
  assert_match('Host: 127.0.0.1:9129', output)
26
34
  end
27
35
 
@@ -1,4 +1,5 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+ require 'set'
2
3
 
3
4
  class TestCurbCurlMulti < Test::Unit::TestCase
4
5
  def teardown
@@ -9,20 +10,58 @@ class TestCurbCurlMulti < Test::Unit::TestCase
9
10
  # for https://github.com/taf2/curb/issues/277
10
11
  # must connect to an external
11
12
  def test_connection_keepalive
12
- # 0123456 default & reserved RubyVM. It will probably include 7 from Dir.glob
13
- lsof=`/usr/bin/which lsof`.strip
14
- open_fds = lambda do
15
- `#{lsof} -p #{Process.pid} | egrep "TCP|UDP" | wc -l`.strip.to_i
13
+ # this test fails with libcurl 7.22.0. I didn't investigate, but it may be related
14
+ # to CURLOPT_MAXCONNECTS bug fixed in 7.30.0:
15
+ # https://github.com/curl/curl/commit/e87e76e2dc108efb1cae87df496416f49c55fca0
16
+ omit("Skip, libcurl too old (< 7.22.0)") if Curl::CURL_VERSION.split('.')[1].to_i <= 22
17
+
18
+ @server.shutdown if @server
19
+ @test_thread.kill if @test_thread
20
+ @server = nil
21
+ File.unlink(locked_file)
22
+ Curl::Multi.autoclose = true
23
+ assert Curl::Multi.autoclose
24
+ # XXX: thought maybe we can clean house here to have the full suite pass in osx... but for now running this test in isolate does pass
25
+ # additionally, if ss allows this to pass on linux without requesting google i think this is a good trade off... leaving some of the thoughts below
26
+ # in hopes that coming back to this later will find it and remember how to fix it
27
+ # types = Set.new
28
+ # close_types = Set.new([TCPServer,TCPSocket,Socket,Curl::Multi, Curl::Easy,WEBrick::Log])
29
+ # ObjectSpace.each_object {|o|
30
+ # if o.respond_to?(:close)
31
+ # types << o.class
32
+ # end
33
+ # if close_types.include?(o.class)
34
+ # o.close
35
+ # end
36
+ # }
37
+ #puts "unique types: #{types.to_a.join("\n")}"
38
+ GC.start # cleanup FDs left over from other tests
39
+ server_setup
40
+ GC.start # cleanup FDs left over from other tests
41
+
42
+ if `which ss`.strip.size == 0
43
+ # osx need lsof still :(
44
+ open_fds = lambda do
45
+ out = `/usr/sbin/lsof -p #{Process.pid} | egrep "TCP|UDP"`# | egrep ':#{TestServlet.port} ' | egrep ESTABLISHED`# | wc -l`.strip.to_i
46
+ #puts out.lines.join("\n")
47
+ out.lines.size
48
+ end
49
+ else
50
+ ss = `which ss`.strip
51
+ open_fds = lambda do
52
+ `#{ss} -n4 state established dport = :#{TestServlet.port} | wc -l`.strip.to_i
53
+ end
16
54
  end
55
+ Curl::Multi.autoclose = false
17
56
  before_open = open_fds.call
57
+ #puts "before_open: #{before_open.inspect}"
18
58
  assert !Curl::Multi.autoclose
19
59
  multi = Curl::Multi.new
20
60
  multi.max_connects = 1 # limit to 1 connection within the multi handle
21
61
 
22
62
  did_complete = false
23
63
  5.times do |n|
24
- # NOTE: we use google here because connecting to our TEST_URL as a local host address appears to not register correctly with lsof as a socket... if anyone knows a better way would be great to not have an external dependency here in the test
25
- easy = Curl::Easy.new("http://google.com/") do |curl|
64
+ easy = Curl::Easy.new(TestServlet.url) do |curl|
26
65
  curl.timeout = 5 # ensure we don't hang for ever connecting to an external host
27
66
  curl.on_complete {
28
67
  did_complete = true
@@ -34,18 +73,19 @@ class TestCurbCurlMulti < Test::Unit::TestCase
34
73
  multi.perform
35
74
  assert did_complete
36
75
  after_open = open_fds.call
37
- assert_equal (after_open - before_open), 1, "with max connections set to 1 at this point the connection to google should still be open"
76
+ #puts "after_open: #{after_open} before_open: #{before_open.inspect}"
77
+ assert_equal 1, (after_open - before_open), "with max connections set to 1 at this point the connection to google should still be open"
38
78
  multi.close
39
79
 
40
80
  after_open = open_fds.call
41
- assert_equal (after_open - before_open), 0, "after closing the multi handle all connections should be closed"
81
+ #puts "after_open: #{after_open} before_open: #{before_open.inspect}"
82
+ assert_equal 0, (after_open - before_open), "after closing the multi handle all connections should be closed"
42
83
 
43
84
  Curl::Multi.autoclose = true
44
85
  multi = Curl::Multi.new
45
86
  did_complete = false
46
87
  5.times do |n|
47
- # NOTE: we use google here because connecting to our TEST_URL as a local host address appears to not register correctly with lsof as a socket... if anyone knows a better way would be great to not have an external dependency here in the test
48
- easy = Curl::Easy.new("http://google.com/") do |curl|
88
+ easy = Curl::Easy.new(TestServlet.url) do |curl|
49
89
  curl.timeout = 5 # ensure we don't hang for ever connecting to an external host
50
90
  curl.on_complete {
51
91
  did_complete = true
@@ -57,7 +97,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
57
97
  multi.perform
58
98
  assert did_complete
59
99
  after_open = open_fds.call
60
- assert_equal (after_open - before_open), 0, "auto close the connections"
100
+ #puts "after_open: #{after_open} before_open: #{before_open.inspect}"
101
+ assert_equal 0, (after_open - before_open), "auto close the connections"
61
102
  ensure
62
103
  Curl::Multi.autoclose = false # restore default
63
104
  end
@@ -0,0 +1,37 @@
1
+ class TestCurbCurlProtocols < Test::Unit::TestCase
2
+ include TestServerMethods
3
+
4
+ def setup
5
+ @easy = Curl::Easy.new
6
+ @easy.set :protocols, Curl::CURLPROTO_HTTP | Curl::CURLPROTO_HTTPS
7
+ @easy.follow_location = true
8
+ server_setup
9
+ end
10
+
11
+ def test_protocol_allowed
12
+ @easy.set :url, "http://127.0.0.1:9129/this_file_does_not_exist.html"
13
+ @easy.perform
14
+ assert_equal 404, @easy.response_code
15
+ end
16
+
17
+ def test_protocol_denied
18
+ @easy.set :url, "gopher://google.com/"
19
+ assert_raises Curl::Err::UnsupportedProtocolError do
20
+ @easy.perform
21
+ end
22
+ end
23
+
24
+ def test_redir_protocol_allowed
25
+ @easy.set :url, TestServlet.url + "/redirect"
26
+ @easy.set :redir_protocols, Curl::CURLPROTO_HTTP
27
+ @easy.perform
28
+ end
29
+
30
+ def test_redir_protocol_denied
31
+ @easy.set :url, TestServlet.url + "/redirect"
32
+ @easy.set :redir_protocols, Curl::CURLPROTO_HTTPS
33
+ assert_raises Curl::Err::UnsupportedProtocolError do
34
+ @easy.perform
35
+ end
36
+ end
37
+ end
data/tests/timeout.rb CHANGED
@@ -17,9 +17,13 @@ class TestCurbTimeouts < Test::Unit::TestCase
17
17
  def test_overall_timeout_on_dead_transfer
18
18
  curl = Curl::Easy.new(wait_url(2))
19
19
  curl.timeout = 1
20
- assert_raise(Curl::Err::TimeoutError) do
20
+ exception = assert_raise(Curl::Err::TimeoutError) do
21
21
  curl.http_get
22
22
  end
23
+ assert_match(
24
+ /^Timeout was reached: Operation timed out after/,
25
+ exception.message
26
+ )
23
27
  end
24
28
 
25
29
  def test_overall_timeout_ms_on_dead_transfer
@@ -44,16 +48,20 @@ class TestCurbTimeouts < Test::Unit::TestCase
44
48
  curl = Curl::Easy.new(serve_url(100, 2, 3))
45
49
  curl.timeout = 1
46
50
  # transfer is aborted despite data being exchanged
47
- assert_raise(Curl::Err::TimeoutError) do
51
+ exception = assert_raise(Curl::Err::TimeoutError) do
48
52
  curl.http_get
49
53
  end
54
+ assert_match(
55
+ /^Timeout was reached: Operation timed out after/,
56
+ exception.message
57
+ )
50
58
  end
51
59
 
52
60
  def test_low_speed_time_on_slow_transfer
53
61
  curl = Curl::Easy.new(serve_url(100, 1, 3))
54
62
  curl.low_speed_time = 2
55
63
  # use default low_speed_limit of 1
56
- assert true, curl.http_get
64
+ assert_equal true, curl.http_get
57
65
  end
58
66
 
59
67
  def test_low_speed_time_on_very_slow_transfer
@@ -63,18 +71,26 @@ class TestCurbTimeouts < Test::Unit::TestCase
63
71
  # XXX for some reason this test fails if low speed limit is not specified
64
72
  curl.low_speed_limit = 1
65
73
  # use default low_speed_limit of 1
66
- assert_raise(Curl::Err::TimeoutError) do
74
+ exception = assert_raise(Curl::Err::TimeoutError) do
67
75
  curl.http_get
68
76
  end
77
+ assert_match(
78
+ /^Timeout was reached: Operation too slow/,
79
+ exception.message
80
+ )
69
81
  end
70
82
 
71
83
  def test_low_speed_limit_on_slow_transfer
72
84
  curl = Curl::Easy.new(serve_url(10, 1, 3))
73
85
  curl.low_speed_time = 2
74
86
  curl.low_speed_limit = 1000
75
- assert_raise(Curl::Err::TimeoutError) do
87
+ exception = assert_raise(Curl::Err::TimeoutError) do
76
88
  curl.http_get
77
89
  end
90
+ assert_match(
91
+ /^Timeout was reached: Operation too slow/,
92
+ exception.message
93
+ )
78
94
  end
79
95
 
80
96
  def test_clearing_low_speed_time
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: curb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.11
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ross Bamford
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-11-02 00:00:00.000000000 Z
12
+ date: 2022-04-29 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Curb (probably CUrl-RuBy or something) provides Ruby-language bindings
15
15
  for the libcurl(3), a fully-featured client-side URL transfer library. cURL and
@@ -70,6 +70,7 @@ files:
70
70
  - tests/tc_curl_maxfilesize.rb
71
71
  - tests/tc_curl_multi.rb
72
72
  - tests/tc_curl_postfield.rb
73
+ - tests/tc_curl_protocols.rb
73
74
  - tests/timeout.rb
74
75
  - tests/timeout_server.rb
75
76
  - tests/unittests.rb
@@ -95,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
96
  - !ruby/object:Gem::Version
96
97
  version: '0'
97
98
  requirements: []
98
- rubygems_version: 3.0.8
99
+ rubygems_version: 3.1.4
99
100
  signing_key:
100
101
  specification_version: 4
101
102
  summary: Ruby libcurl bindings
@@ -113,6 +114,7 @@ test_files:
113
114
  - tests/timeout.rb
114
115
  - tests/bug_crash_on_debug.rb
115
116
  - tests/unittests.rb
117
+ - tests/tc_curl_protocols.rb
116
118
  - tests/bug_issue102.rb
117
119
  - tests/bug_curb_easy_blocks_ruby_threads.rb
118
120
  - tests/bug_multi_segfault.rb