curb 0.9.4 → 0.9.10

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
- SHA1:
3
- metadata.gz: f745e8a670b83facff3af44169290e38d59fc087
4
- data.tar.gz: 23258e4b6f84aaec880ffb050c8fd774f82728a7
2
+ SHA256:
3
+ metadata.gz: 7fd2f75fea9421bc6361cd44ea7343f42867eb1f87177e20598be60548e68a07
4
+ data.tar.gz: e30c3e3ba5e406d5517ad89f728f0c4a95682c9ec750fc93ce3fb381da5ac2cc
5
5
  SHA512:
6
- metadata.gz: 4f79f4530412869086922114a4a9170acd57e5a8b68c43b19616a3d31609d9f85ef94bf890cad9b57cdd4f7a59bf1257eeb7d20ca40781e72ec4a2f03fe91711
7
- data.tar.gz: 5f67114460a69c76e6e7835124e26825833c8de153232ba10bab032732481e2c694baf54976dcba80cd43fce37a75f1267a9e9c507d369dead6c924ebb829b67
6
+ metadata.gz: 6140588c19e97ce62b9c180fe07355f4781adfa84d3066402378b3abc995c5e23d1abc7458e4575a91964c8ea9fe572276a73a1dab8447de8607325a3fc9463f
7
+ data.tar.gz: 88ef4f8b18028a2d6e695a543a624d1cd468482cbc06c13b94c94504a30711b9ff0b1d45b7c2f0af145a9114d1bef1975e5889f63fdd0e6387c2c86912394270
data/README.markdown CHANGED
@@ -7,19 +7,35 @@ 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
8
  cURL and libcurl live at [http://curl.haxx.se/](http://curl.haxx.se/) .
9
9
 
10
- Curb is a work-in-progress, and currently only supports libcurl's 'easy' and 'multi' modes.
10
+ Curb is a work-in-progress, and currently only supports libcurl's `easy` and `multi` modes.
11
11
 
12
12
  ## License
13
13
 
14
- Curb is copyright (c)2006 Ross Bamford, and released under the terms of the
15
- Ruby license. See the LICENSE file for the gory details.
14
+ Curb is copyright (c)2006 Ross Bamford, and released under the terms of the
15
+ Ruby license. See the LICENSE file for the gory details.
16
16
 
17
17
  ## You will need
18
18
 
19
- * A working Ruby installation (1.8+, tested with 1.8.6, 1.8.7, 1.9.1, and 1.9.2)
20
- * A working (lib)curl installation, with development stuff (7.5+, tested with 7.19.x)
19
+ * A working Ruby installation (`1.8.7+` will work but `2.1+` preferred)
20
+ * A working libcurl development installation
21
+ (Ideally one of the versions listed in the compatibility chart below that maps to your `curb` version)
21
22
  * A sane build environment (e.g. gcc, make)
22
23
 
24
+ ## Version Compatibility chart
25
+
26
+ A **non-exhaustive** set of compatibility versions of the libcurl library
27
+ with this gem are as follows. (Note that these are only the ones that have been
28
+ tested and reported to work across a variety of platforms / rubies)
29
+
30
+ | Gem Version | Release Date | libcurl versions |
31
+ | ----------- | ----------- | ---------------- |
32
+ | 0.9.8 | Jan 2019 | 7.58 - 7.63 |
33
+ | 0.9.7 | Nov 2018 | 7.56 - 7.60 |
34
+ | 0.9.6 | May 2018 | 7.51 - 7.59 |
35
+ | 0.9.5 | May 2018 | 7.51 - 7.59 |
36
+ | 0.9.4 | Aug 2017 | 7.41 - 7.58 |
37
+ | 0.9.3 | Apr 2016 | 7.26 - 7.58 |
38
+
23
39
  ## Installation...
24
40
 
25
41
  ... will usually be as simple as:
@@ -30,19 +46,23 @@ On Windows, make sure you're using the [DevKit](http://rubyinstaller.org/downloa
30
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
31
47
  line (alter paths to your curl location, but remember to use forward slashes):
32
48
 
33
- gem install curb --platform=ruby -- --with-curl-lib=C:/curl-7.39.0-devel-mingw32/bin --with-curl-include=C:/curl-7.39.0-devel-mingw32/include
49
+ 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
50
+
51
+ Note that with Windows moving from one method of compiling to another as of Ruby `2.4` (DevKit -> MYSYS2),
52
+ the usage of Ruby `2.4+` with this gem on windows is unlikely to work. It is advised to use the
53
+ latest version of Ruby 2.3 available [HERE](https://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.3.3.exe)
34
54
 
35
- Or, if you downloaded the archive:
55
+ Or, if you downloaded the archive:
36
56
 
37
- $ rake install
57
+ $ rake compile && rake install
38
58
 
39
59
  If you have a weird setup, you might need extconf options. In this case, pass
40
60
  them like so:
41
61
 
42
- $ rake install EXTCONF_OPTS='--with-curl-dir=/path/to/libcurl --prefix=/what/ever'
43
-
62
+ $ rake compile EXTCONF_OPTS='--with-curl-dir=/path/to/libcurl --prefix=/what/ever' && rake install
63
+
44
64
  Curb is tested only on GNU/Linux x86 and Mac OSX - YMMV on other platforms.
45
- If you do use another platform and experience problems, or if you can
65
+ If you do use another platform and experience problems, or if you can
46
66
  expand on the above instructions, please report the issue at http://github.com/taf2/curb/issues
47
67
 
48
68
  On Ubuntu, the dependencies can be satisfied by installing the following packages:
@@ -52,7 +72,7 @@ On Ubuntu, the dependencies can be satisfied by installing the following package
52
72
  On RedHat:
53
73
 
54
74
  $ sudo yum install ruby-devel libcurl-devel openssl-devel
55
-
75
+
56
76
  Curb has fairly extensive RDoc comments in the source. You can build the
57
77
  documentation with:
58
78
 
@@ -80,7 +100,7 @@ puts http.body_str
80
100
  http = Curl.post("http://www.google.com/", {:foo => "bar"})
81
101
  puts http.body_str
82
102
 
83
- http = Curl.get("http://www.google.com/") do|http|
103
+ http = Curl.get("http://www.google.com/") do |http|
84
104
  http.headers['Cookie'] = 'foo=1;bar=2'
85
105
  end
86
106
  puts http.body_str
@@ -104,7 +124,7 @@ puts c.body_str
104
124
  ### Additional config:
105
125
 
106
126
  ```ruby
107
- Curl::Easy.perform("http://www.google.co.uk") do |curl|
127
+ Curl::Easy.perform("http://www.google.co.uk") do |curl|
108
128
  curl.headers["User-Agent"] = "myapp-0.0"
109
129
  curl.verbose = true
110
130
  end
@@ -113,7 +133,7 @@ end
113
133
  Same thing, more manual:
114
134
 
115
135
  ```ruby
116
- c = Curl::Easy.new("http://www.google.co.uk") do |curl|
136
+ c = Curl::Easy.new("http://www.google.co.uk") do |curl|
117
137
  curl.headers["User-Agent"] = "myapp-0.0"
118
138
  curl.verbose = true
119
139
  end
@@ -134,9 +154,9 @@ c.perform
134
154
  ### HTTP "insecure" SSL connections (like curl -k, --insecure) to avoid Curl::Err::SSLCACertificateError:
135
155
 
136
156
  ```ruby
137
- c = Curl::Easy.new("http://github.com/")
138
- c.ssl_verify_peer = false
139
- c.perform
157
+ c = Curl::Easy.new("https://github.com/")
158
+ c.ssl_verify_peer = false
159
+ c.perform
140
160
  ```
141
161
 
142
162
  ### Supplying custom handlers:
@@ -194,7 +214,7 @@ puts (c.body_str.include? "You are using HTTP/2 right now!") ? "HTTP/2" : "HTTP/
194
214
  # make multiple GET requests
195
215
  easy_options = {:follow_location => true}
196
216
  # Use Curl::CURLPIPE_MULTIPLEX for HTTP/2 multiplexing
197
- multi_options = {:pipeline => Curl::CURLPIPE_HTTP1}
217
+ multi_options = {:pipeline => Curl::CURLPIPE_HTTP1}
198
218
 
199
219
  Curl::Multi.get(['url1','url2','url3','url4','url5'], easy_options, multi_options) do|easy|
200
220
  # do something interesting with the easy response
data/Rakefile CHANGED
@@ -2,11 +2,6 @@
2
2
  #
3
3
  require 'rake/clean'
4
4
  require 'rake/testtask'
5
- begin
6
- require 'rdoc/task'
7
- rescue LoadError => e
8
- require 'rake/rdoctask'
9
- end
10
5
 
11
6
  CLEAN.include '**/*.o'
12
7
  CLEAN.include "**/*.#{(defined?(RbConfig) ? RbConfig : Config)::MAKEFILE_CONFIG['DLEXT']}"
@@ -15,6 +10,22 @@ CLOBBER.include '**/*.log'
15
10
  CLOBBER.include '**/Makefile'
16
11
  CLOBBER.include '**/extconf.h'
17
12
 
13
+ # Not available for really old rubies, but that's ok.
14
+ begin
15
+ require 'pry'
16
+ rescue LoadError
17
+ puts "Failed to load pry."
18
+ end
19
+
20
+ # Load support ruby and rake files (in this order)
21
+ Dir.glob('tasks/*.rb').each { |r| load r}
22
+ Dir.glob('tasks/*.rake').each { |r| load r}
23
+
24
+ desc 'Print Ruby major version (ie "2_5")'
25
+ task :ruby_version do
26
+ print current_ruby_major
27
+ end
28
+
18
29
  def announce(msg='')
19
30
  $stderr.puts msg
20
31
  end
@@ -43,12 +54,11 @@ end
43
54
  make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
44
55
  MAKECMD = ENV['MAKE_CMD'] || make_program
45
56
  MAKEOPTS = ENV['MAKE_OPTS'] || ''
46
-
47
57
  CURB_SO = "ext/curb_core.#{(defined?(RbConfig) ? RbConfig : Config)::MAKEFILE_CONFIG['DLEXT']}"
48
58
 
49
59
  file 'ext/Makefile' => 'ext/extconf.rb' do
50
60
  Dir.chdir('ext') do
51
- ruby "extconf.rb #{ENV['EXTCONF_OPTS']}"
61
+ shell('ruby', 'extconf.rb', ENV['EXTCONF_OPTS'].to_s, live_stdout: STDOUT)
52
62
  end
53
63
  end
54
64
 
@@ -89,12 +99,12 @@ if ENV['RELTEST']
89
99
  else
90
100
  task :alltests => [:unittests, :bugtests]
91
101
  end
92
-
102
+
93
103
  Rake::TestTask.new(:unittests) do |t|
94
104
  t.test_files = FileList['tests/tc_*.rb']
95
105
  t.verbose = false
96
106
  end
97
-
107
+
98
108
  Rake::TestTask.new(:bugtests) do |t|
99
109
  t.test_files = FileList['tests/bug_*.rb']
100
110
  t.verbose = false
@@ -136,6 +146,12 @@ end
136
146
 
137
147
  desc "Publish the RDoc documentation to project web site"
138
148
  task :doc_upload => [ :doc ] do
149
+ begin
150
+ require 'rdoc/task'
151
+ rescue LoadError => e
152
+ require 'rake/rdoctask'
153
+ end
154
+
139
155
  if ENV['RELTEST']
140
156
  announce "Release Task Testing, skipping doc upload"
141
157
  else
@@ -170,7 +186,7 @@ else
170
186
  spec_source = File.read File.join(File.dirname(__FILE__),'curb.gemspec')
171
187
  spec = nil
172
188
  # see: http://gist.github.com/16215
173
- Thread.new { spec = eval("$SAFE = 3\n#{spec_source}") }.join
189
+ Thread.new { spec = eval("#{spec_source}") }.join
174
190
  spec.validate
175
191
  Gem::Package.build(spec)
176
192
  end
data/ext/curb.c CHANGED
@@ -272,15 +272,15 @@ void Init_curb_core() {
272
272
  /* Passed to on_debug handler to indicate that the data is protocol data sent to the peer. */
273
273
  rb_define_const(mCurl, "CURLINFO_DATA_OUT", LONG2NUM(CURLINFO_DATA_OUT));
274
274
 
275
- #ifdef HAVE_CURLFTPMETHOD_MULTICWD
275
+ #ifdef HAVE_CURLFTPMETHOD_MULTICWD
276
276
  rb_define_const(mCurl, "CURL_MULTICWD", LONG2NUM(CURLFTPMETHOD_MULTICWD));
277
277
  #endif
278
278
 
279
- #ifdef HAVE_CURLFTPMETHOD_NOCWD
279
+ #ifdef HAVE_CURLFTPMETHOD_NOCWD
280
280
  rb_define_const(mCurl, "CURL_NOCWD", LONG2NUM(CURLFTPMETHOD_NOCWD));
281
281
  #endif
282
282
 
283
- #ifdef HAVE_CURLFTPMETHOD_SINGLECWD
283
+ #ifdef HAVE_CURLFTPMETHOD_SINGLECWD
284
284
  rb_define_const(mCurl, "CURL_SINGLECWD", LONG2NUM(CURLFTPMETHOD_SINGLECWD));
285
285
  #endif
286
286
 
@@ -296,13 +296,13 @@ void Init_curb_core() {
296
296
  rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1", LONG2NUM(CURL_SSLVERSION_TLSv1));
297
297
  rb_define_const(mCurl, "CURL_SSLVERSION_SSLv2", LONG2NUM(CURL_SSLVERSION_SSLv2));
298
298
  rb_define_const(mCurl, "CURL_SSLVERSION_SSLv3", LONG2NUM(CURL_SSLVERSION_SSLv3));
299
- #if HAVE_CURL_SSLVERSION_TLSv1_0
299
+ #if HAVE_CURL_SSLVERSION_TLSV1_0
300
300
  rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_0", LONG2NUM(CURL_SSLVERSION_TLSv1_0));
301
301
  #endif
302
- #if HAVE_CURL_SSLVERSION_TLSv1_1
302
+ #if HAVE_CURL_SSLVERSION_TLSV1_1
303
303
  rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_1", LONG2NUM(CURL_SSLVERSION_TLSv1_1));
304
304
  #endif
305
- #if HAVE_CURL_SSLVERSION_TLSv1_2
305
+ #if HAVE_CURL_SSLVERSION_TLSV1_2
306
306
  rb_define_const(mCurl, "CURL_SSLVERSION_TLSv1_2", LONG2NUM(CURL_SSLVERSION_TLSv1_2));
307
307
  #endif
308
308
 
@@ -352,6 +352,13 @@ void Init_curb_core() {
352
352
  rb_define_const(mCurl, "CURLPROXY_SOCKS5", LONG2NUM(-2));
353
353
  #endif
354
354
 
355
+ /* When passed to Curl::Easy#proxy_type , indicates that the proxy is a SOCKS5 proxy (and that the proxy should resolve the hostname). (libcurl >= 7.17.2) */
356
+ #ifdef HAVE_CURLPROXY_SOCKS5_HOSTNAME
357
+ rb_define_const(mCurl, "CURLPROXY_SOCKS5_HOSTNAME", LONG2NUM(CURLPROXY_SOCKS5_HOSTNAME));
358
+ #else
359
+ rb_define_const(mCurl, "CURLPROXY_SOCKS5_HOSTNAME", LONG2NUM(-2));
360
+ #endif
361
+
355
362
  /* When passed to Curl::Easy#http_auth_types or Curl::Easy#proxy_auth_types, directs libcurl to use Basic authentication. */
356
363
  #ifdef HAVE_CURLAUTH_BASIC
357
364
  rb_define_const(mCurl, "CURLAUTH_BASIC", LONG2NUM(CURLAUTH_BASIC));
@@ -585,6 +592,9 @@ void Init_curb_core() {
585
592
  CURB_DEFINE(CURLOPT_REFERER);
586
593
  CURB_DEFINE(CURLOPT_USERAGENT);
587
594
  CURB_DEFINE(CURLOPT_HTTPHEADER);
595
+ #if HAVE_CURLOPT_PROXYHEADER
596
+ CURB_DEFINE(CURLOPT_PROXYHEADER);
597
+ #endif
588
598
  #if HAVE_CURLOPT_HTTP200ALIASES
589
599
  CURB_DEFINE(CURLOPT_HTTP200ALIASES);
590
600
  #endif
@@ -609,6 +619,9 @@ void Init_curb_core() {
609
619
  #if LIBCURL_VERSION_NUM >= 0x072100 /* 7.33.0 */
610
620
  CURB_DEFINE(CURL_HTTP_VERSION_2_0);
611
621
  #endif
622
+ #if LIBCURL_VERSION_NUM >= 0x072f00 /* 7.47.0 */
623
+ CURB_DEFINE(CURL_HTTP_VERSION_2TLS);
624
+ #endif
612
625
  #if HAVE_CURLOPT_IGNORE_CONTENT_LENGTH
613
626
  CURB_DEFINE(CURLOPT_IGNORE_CONTENT_LENGTH);
614
627
  #endif
@@ -1023,6 +1036,20 @@ void Init_curb_core() {
1023
1036
  CURB_DEFINE(CURLOPT_UNIX_SOCKET_PATH);
1024
1037
  #endif
1025
1038
 
1039
+ #if HAVE_CURLOPT_PIPEWAIT
1040
+ CURB_DEFINE(CURLOPT_PIPEWAIT);
1041
+ #endif
1042
+
1043
+ #if HAVE_CURLOPT_TCP_KEEPALIVE
1044
+ CURB_DEFINE(CURLOPT_TCP_KEEPALIVE);
1045
+ CURB_DEFINE(CURLOPT_TCP_KEEPIDLE);
1046
+ CURB_DEFINE(CURLOPT_TCP_KEEPINTVL);
1047
+ #endif
1048
+
1049
+ #if HAVE_CURLOPT_HAPROXYPROTOCOL
1050
+ CURB_DEFINE(CURLOPT_HAPROXYPROTOCOL);
1051
+ #endif
1052
+
1026
1053
  #if LIBCURL_VERSION_NUM >= 0x072B00 /* 7.43.0 */
1027
1054
  CURB_DEFINE(CURLPIPE_NOTHING);
1028
1055
  CURB_DEFINE(CURLPIPE_HTTP1);
data/ext/curb.h CHANGED
@@ -9,6 +9,13 @@
9
9
  #define __CURB_H
10
10
 
11
11
  #include <ruby.h>
12
+
13
+ #ifdef HAVE_RUBY_IO_H
14
+ #include "ruby/io.h"
15
+ #else
16
+ #include "rubyio.h" // ruby 1.8
17
+ #endif
18
+
12
19
  #include <curl/curl.h>
13
20
 
14
21
  #include "curb_config.h"
@@ -20,11 +27,11 @@
20
27
  #include "curb_macros.h"
21
28
 
22
29
  // These should be managed from the Rake 'release' task.
23
- #define CURB_VERSION "0.9.4"
24
- #define CURB_VER_NUM 904
30
+ #define CURB_VERSION "0.9.10"
31
+ #define CURB_VER_NUM 9010
25
32
  #define CURB_VER_MAJ 0
26
- #define CURB_VER_MIN 4
27
- #define CURB_VER_MIC 0
33
+ #define CURB_VER_MIN 9
34
+ #define CURB_VER_MIC 10
28
35
  #define CURB_VER_PATCH 0
29
36
 
30
37
 
data/ext/curb_easy.c CHANGED
@@ -25,12 +25,18 @@ static VALUE rbstrAmp;
25
25
 
26
26
  VALUE cCurlEasy;
27
27
 
28
+ // for Ruby 1.8
29
+ #ifndef HAVE_RB_IO_STDIO_FILE
30
+ static FILE * rb_io_stdio_file(rb_io_t *fptr) {
31
+ return fptr->f;
32
+ }
33
+ #endif
28
34
 
29
35
  /* ================== CURL HANDLER FUNCS ==============*/
30
36
 
31
37
  static VALUE callback_exception(VALUE unused) {
32
38
  return Qfalse;
33
- }
39
+ }
34
40
 
35
41
  /* These handle both body and header data */
36
42
  static size_t default_data_handler(char *stream,
@@ -223,10 +229,18 @@ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
223
229
  curl_slist_free_all(rbce->curl_headers);
224
230
  }
225
231
 
232
+ if (rbce->curl_proxy_headers) {
233
+ curl_slist_free_all(rbce->curl_proxy_headers);
234
+ }
235
+
226
236
  if (rbce->curl_ftp_commands) {
227
237
  curl_slist_free_all(rbce->curl_ftp_commands);
228
238
  }
229
239
 
240
+ if (rbce->curl_resolve) {
241
+ curl_slist_free_all(rbce->curl_resolve);
242
+ }
243
+
230
244
  if (rbce->curl) {
231
245
  /* disable any progress or debug events */
232
246
  curl_easy_setopt(rbce->curl, CURLOPT_WRITEFUNCTION, NULL);
@@ -239,6 +253,7 @@ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
239
253
  curl_easy_setopt(rbce->curl, CURLOPT_PROGRESSFUNCTION, NULL);
240
254
  curl_easy_setopt(rbce->curl, CURLOPT_NOPROGRESS, 1);
241
255
  curl_easy_cleanup(rbce->curl);
256
+ rbce->curl = NULL;
242
257
  }
243
258
  }
244
259
 
@@ -254,7 +269,9 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
254
269
  rbce->opts = rb_hash_new();
255
270
 
256
271
  rbce->curl_headers = NULL;
272
+ rbce->curl_proxy_headers = NULL;
257
273
  rbce->curl_ftp_commands = NULL;
274
+ rbce->curl_resolve = NULL;
258
275
 
259
276
  /* various-typed opts */
260
277
  rbce->local_port = 0;
@@ -272,6 +289,8 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
272
289
  rbce->ftp_response_timeout = 0;
273
290
  rbce->low_speed_limit = 0;
274
291
  rbce->low_speed_time = 0;
292
+ rbce->max_send_speed_large = 0;
293
+ rbce->max_recv_speed_large = 0;
275
294
  rbce->ssl_version = -1;
276
295
  rbce->use_ssl = -1;
277
296
  rbce->ftp_filemethod = -1;
@@ -293,25 +312,37 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
293
312
  rbce->callback_active = 0;
294
313
  }
295
314
 
315
+ /*
316
+ * Allocate space for a Curl::Easy instance.
317
+ */
318
+ static VALUE ruby_curl_easy_allocate(VALUE klass) {
319
+ ruby_curl_easy *rbce;
320
+ rbce = ALLOC(ruby_curl_easy);
321
+ rbce->curl = NULL;
322
+ rbce->opts = Qnil;
323
+ rbce->multi = Qnil;
324
+ ruby_curl_easy_zero(rbce);
325
+ return Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
326
+ }
327
+
296
328
  /*
297
329
  * call-seq:
298
330
  * Curl::Easy.new => #&lt;Curl::Easy...&gt;
299
331
  * Curl::Easy.new(url = nil) => #&lt;Curl::Easy...&gt;
300
332
  * Curl::Easy.new(url = nil) { |self| ... } => #&lt;Curl::Easy...&gt;
301
333
  *
302
- * Create a new Curl::Easy instance, optionally supplying the URL.
334
+ * Initialize a new Curl::Easy instance, optionally supplying the URL.
303
335
  * The block form allows further configuration to be supplied before
304
336
  * the instance is returned.
305
337
  */
306
- static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
338
+ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
307
339
  CURLcode ecode;
308
340
  VALUE url, blk;
309
- VALUE new_curl;
310
341
  ruby_curl_easy *rbce;
311
342
 
312
343
  rb_scan_args(argc, argv, "01&", &url, &blk);
313
344
 
314
- rbce = ALLOC(ruby_curl_easy);
345
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
315
346
 
316
347
  /* handler */
317
348
  rbce->curl = curl_easy_init();
@@ -319,8 +350,6 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
319
350
  rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle");
320
351
  }
321
352
 
322
- new_curl = Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
323
-
324
353
  rbce->multi = Qnil;
325
354
  rbce->opts = Qnil;
326
355
 
@@ -328,17 +357,18 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
328
357
 
329
358
  rb_easy_set("url", url);
330
359
 
331
- /* set the new_curl pointer to the curl handle */
332
- ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)new_curl);
360
+
361
+ /* set the pointer to the curl handle */
362
+ ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
333
363
  if (ecode != CURLE_OK) {
334
364
  raise_curl_easy_error_exception(ecode);
335
365
  }
336
366
 
337
367
  if (blk != Qnil) {
338
- rb_funcall(blk, idCall, 1, new_curl);
368
+ rb_funcall(blk, idCall, 1, self);
339
369
  }
340
370
 
341
- return new_curl;
371
+ return self;
342
372
  }
343
373
 
344
374
  /*
@@ -358,7 +388,9 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
358
388
  memcpy(newrbce, rbce, sizeof(ruby_curl_easy));
359
389
  newrbce->curl = curl_easy_duphandle(rbce->curl);
360
390
  newrbce->curl_headers = NULL;
391
+ newrbce->curl_proxy_headers = NULL;
361
392
  newrbce->curl_ftp_commands = NULL;
393
+ newrbce->curl_resolve = NULL;
362
394
 
363
395
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
364
396
  }
@@ -368,7 +400,7 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
368
400
  * easy.close => nil
369
401
  *
370
402
  * Close the Curl::Easy instance. Any open connections are closed
371
- * The easy handle is reinitialized. If a previous multi handle was
403
+ * The easy handle is reinitialized. If a previous multi handle was
372
404
  * open it is set to nil and will be cleared after a GC.
373
405
  */
374
406
  static VALUE ruby_curl_easy_close(VALUE self) {
@@ -441,6 +473,12 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
441
473
  rbce->curl_headers = NULL;
442
474
  }
443
475
 
476
+ /* Free everything up */
477
+ if (rbce->curl_proxy_headers) {
478
+ curl_slist_free_all(rbce->curl_proxy_headers);
479
+ rbce->curl_proxy_headers = NULL;
480
+ }
481
+
444
482
  return opts_dup;
445
483
  }
446
484
 
@@ -493,6 +531,10 @@ static VALUE ruby_curl_easy_headers_set(VALUE self, VALUE headers) {
493
531
  CURB_OBJECT_HSETTER(ruby_curl_easy, headers);
494
532
  }
495
533
 
534
+ static VALUE ruby_curl_easy_proxy_headers_set(VALUE self, VALUE proxy_headers) {
535
+ CURB_OBJECT_HSETTER(ruby_curl_easy, proxy_headers);
536
+ }
537
+
496
538
  /*
497
539
  * call-seq:
498
540
  * easy.headers => Hash, Array or Str
@@ -503,11 +545,46 @@ static VALUE ruby_curl_easy_headers_get(VALUE self) {
503
545
  ruby_curl_easy *rbce;
504
546
  VALUE headers;
505
547
  Data_Get_Struct(self, ruby_curl_easy, rbce);
506
- headers = rb_easy_get("headers");//rb_hash_aref(rbce->opts, rb_intern("headers"));
548
+ headers = rb_easy_get("headers");//rb_hash_aref(rbce->opts, rb_intern("headers"));
507
549
  if (headers == Qnil) { headers = rb_easy_set("headers", rb_hash_new()); }
508
550
  return headers;
509
551
  }
510
552
 
553
+ /*
554
+ * call-seq:
555
+ * easy.proxy_headers = "Header: val" => "Header: val"
556
+ * easy.proxy_headers = {"Header" => "val" ..., "Header" => "val"} => {"Header: val", ...}
557
+ * easy.proxy_headers = ["Header: val" ..., "Header: val"] => ["Header: val", ...]
558
+ *
559
+ *
560
+ * For example to set a standard or custom header:
561
+ *
562
+ * easy.proxy_headers["MyHeader"] = "myval"
563
+ *
564
+ * To remove a standard header (this is useful when removing libcurls default
565
+ * 'Expect: 100-Continue' header when using HTTP form posts):
566
+ *
567
+ * easy.proxy_headers["Expect"] = ''
568
+ *
569
+ * Anything passed to libcurl as a header will be converted to a string during
570
+ * the perform step.
571
+ */
572
+
573
+ /*
574
+ * call-seq:
575
+ * easy.proxy_headers => Hash, Array or Str
576
+ *
577
+ * Obtain the custom HTTP proxy_headers for following requests.
578
+ */
579
+ static VALUE ruby_curl_easy_proxy_headers_get(VALUE self) {
580
+ ruby_curl_easy *rbce;
581
+ VALUE proxy_headers;
582
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
583
+ proxy_headers = rb_easy_get("proxy_headers");//rb_hash_aref(rbce->opts, rb_intern("proxy_headers"));
584
+ if (proxy_headers == Qnil) { proxy_headers = rb_easy_set("proxy_headers", rb_hash_new()); }
585
+ return proxy_headers;
586
+ }
587
+
511
588
  /*
512
589
  * call-seq:
513
590
  * easy.interface => string
@@ -715,29 +792,29 @@ static VALUE ruby_curl_easy_useragent_get(VALUE self) {
715
792
  /*
716
793
  * call-seq:
717
794
  * easy.post_body = "some=form%20data&to=send" => string or nil
718
- *
795
+ *
719
796
  * Sets the POST body of this Curl::Easy instance. This is expected to be
720
797
  * URL encoded; no additional processing or encoding is done on the string.
721
798
  * The content-type header will be set to application/x-www-form-urlencoded.
722
- *
799
+ *
723
800
  * This is handy if you want to perform a POST against a Curl::Multi instance.
724
801
  */
725
802
  static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
726
803
  ruby_curl_easy *rbce;
727
804
  CURL *curl;
728
-
805
+
729
806
  char *data;
730
807
  long len;
731
808
 
732
809
  Data_Get_Struct(self, ruby_curl_easy, rbce);
733
-
810
+
734
811
  curl = rbce->curl;
735
-
812
+
736
813
  if ( post_body == Qnil ) {
737
814
  rb_easy_del("postdata_buffer");
738
815
  curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
739
-
740
- } else {
816
+
817
+ } else {
741
818
  if (rb_type(post_body) == T_STRING) {
742
819
  data = StringValuePtr(post_body);
743
820
  len = RSTRING_LEN(post_body);
@@ -750,19 +827,19 @@ static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
750
827
  else {
751
828
  rb_raise(rb_eRuntimeError, "post data must respond_to .to_s");
752
829
  }
753
-
754
- // Store the string, since it has to hang around for the duration of the
830
+
831
+ // Store the string, since it has to hang around for the duration of the
755
832
  // request. See CURLOPT_POSTFIELDS in the libcurl docs.
756
833
  //rbce->postdata_buffer = post_body;
757
834
  rb_easy_set("postdata_buffer", post_body);
758
-
835
+
759
836
  curl_easy_setopt(curl, CURLOPT_POST, 1);
760
837
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
761
838
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
762
-
839
+
763
840
  return post_body;
764
841
  }
765
-
842
+
766
843
  return Qnil;
767
844
  }
768
845
 
@@ -779,7 +856,7 @@ static VALUE ruby_curl_easy_post_body_get(VALUE self) {
779
856
  /*
780
857
  * call-seq:
781
858
  * easy.put_data = data => ""
782
- *
859
+ *
783
860
  * Points this Curl::Easy instance to data to be uploaded via PUT. This
784
861
  * sets the request to a PUT type request - useful if you want to PUT via
785
862
  * a multi handle.
@@ -811,7 +888,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
811
888
  curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
812
889
  #endif
813
890
 
814
- /*
891
+ /*
815
892
  * we need to set specific headers for the PUT to work... so
816
893
  * convert the internal headers structure to a HASH if one is set
817
894
  */
@@ -825,7 +902,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
825
902
  if (NIL_P(data)) { return data; }
826
903
 
827
904
  headers = rb_easy_get("headers");
828
- if( headers == Qnil ) {
905
+ if( headers == Qnil ) {
829
906
  headers = rb_hash_new();
830
907
  }
831
908
 
@@ -880,6 +957,25 @@ static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) {
880
957
  CURB_OBJECT_HGETTER(ruby_curl_easy, ftp_commands);
881
958
  }
882
959
 
960
+ /*
961
+ * call-seq:
962
+ * easy.resolve = [ "example.com:80:127.0.0.1" ] => [ "example.com:80:127.0.0.1" ]
963
+ *
964
+ * Set the resolve list to statically resolve hostnames to IP addresses,
965
+ * bypassing DNS for matching hostname/port combinations.
966
+ */
967
+ static VALUE ruby_curl_easy_resolve_set(VALUE self, VALUE resolve) {
968
+ CURB_OBJECT_HSETTER(ruby_curl_easy, resolve);
969
+ }
970
+
971
+ /*
972
+ * call-seq
973
+ * easy.resolve => array or nil
974
+ */
975
+ static VALUE ruby_curl_easy_resolve_get(VALUE self) {
976
+ CURB_OBJECT_HGETTER(ruby_curl_easy, resolve);
977
+ }
978
+
883
979
  /* ================== IMMED ATTRS ==================*/
884
980
 
885
981
  /*
@@ -990,7 +1086,7 @@ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) {
990
1086
  (!strncmp("ntlm",node,4)) ? CURLAUTH_NTLM : \
991
1087
  (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : \
992
1088
  (!strncmp("any",node,3)) ? CURLAUTH_ANY : 0
993
- #else
1089
+ #else
994
1090
  #define CURL_HTTPAUTH_STR_TO_NUM(node) \
995
1091
  (!strncmp("basic",node,5)) ? CURLAUTH_BASIC : \
996
1092
  (!strncmp("digest",node,6)) ? CURLAUTH_DIGEST : \
@@ -1022,7 +1118,7 @@ static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE sel
1022
1118
 
1023
1119
  if (len == 1 && (rb_ary_entry(args_ary,0) == Qnil || TYPE(rb_ary_entry(args_ary,0)) == T_FIXNUM ||
1024
1120
  TYPE(rb_ary_entry(args_ary,0)) == T_BIGNUM)) {
1025
- if (rb_ary_entry(args_ary,0) == Qnil) {
1121
+ if (rb_ary_entry(args_ary,0) == Qnil) {
1026
1122
  rbce->http_auth_types = 0;
1027
1123
  }
1028
1124
  else {
@@ -1104,7 +1200,7 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1104
1200
 
1105
1201
  /*
1106
1202
  * call-seq:
1107
- * easy.timeout = fixnum or nil => fixnum or nil
1203
+ * easy.timeout = float, fixnum or nil => numeric
1108
1204
  *
1109
1205
  * Set the maximum time in seconds that you allow the libcurl transfer
1110
1206
  * operation to take. Normally, name lookups can take a considerable time
@@ -1113,20 +1209,39 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1113
1209
  *
1114
1210
  * Set to nil (or zero) to disable timeout (it will then only timeout
1115
1211
  * on the system's internal timeouts).
1212
+ *
1213
+ * Uses timeout_ms internally instead of timeout because it allows for
1214
+ * better precision and libcurl will use the last set value when both
1215
+ * timeout and timeout_ms are set.
1216
+ *
1116
1217
  */
1117
- static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout) {
1118
- CURB_IMMED_SETTER(ruby_curl_easy, timeout, 0);
1218
+ static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout_s) {
1219
+ ruby_curl_easy *rbce;
1220
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1221
+
1222
+ if (Qnil == timeout_s || NUM2DBL(timeout_s) <= 0.0) {
1223
+ rbce->timeout_ms = 0;
1224
+ } else {
1225
+ rbce->timeout_ms = (unsigned long)(NUM2DBL(timeout_s) * 1000);
1226
+ }
1227
+
1228
+ return DBL2NUM(rbce->timeout_ms / 1000.0);
1119
1229
  }
1120
1230
 
1121
1231
  /*
1122
1232
  * call-seq:
1123
- * easy.timeout => fixnum or nil
1233
+ * easy.timeout => numeric
1124
1234
  *
1125
1235
  * Obtain the maximum time in seconds that you allow the libcurl transfer
1126
1236
  * operation to take.
1237
+ *
1238
+ * Uses timeout_ms internally instead of timeout.
1239
+ *
1127
1240
  */
1128
- static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1129
- CURB_IMMED_GETTER(ruby_curl_easy, timeout, 0);
1241
+ static VALUE ruby_curl_easy_timeout_get(VALUE self) {
1242
+ ruby_curl_easy *rbce;
1243
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1244
+ return DBL2NUM(rbce->timeout_ms / 1000.0);
1130
1245
  }
1131
1246
 
1132
1247
  /*
@@ -1142,7 +1257,16 @@ static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1142
1257
  * on the system's internal timeouts).
1143
1258
  */
1144
1259
  static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1145
- CURB_IMMED_SETTER(ruby_curl_easy, timeout_ms, 0);
1260
+ ruby_curl_easy *rbce;
1261
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1262
+
1263
+ if (Qnil == timeout_ms || NUM2DBL(timeout_ms) <= 0.0) {
1264
+ rbce->timeout_ms = 0;
1265
+ } else {
1266
+ rbce->timeout_ms = NUM2ULONG(timeout_ms);
1267
+ }
1268
+
1269
+ return ULONG2NUM(rbce->timeout_ms);
1146
1270
  }
1147
1271
 
1148
1272
  /*
@@ -1152,8 +1276,10 @@ static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1152
1276
  * Obtain the maximum time in milliseconds that you allow the libcurl transfer
1153
1277
  * operation to take.
1154
1278
  */
1155
- static VALUE ruby_curl_easy_timeout_ms_get(VALUE self, VALUE timeout_ms) {
1156
- CURB_IMMED_GETTER(ruby_curl_easy, timeout_ms, 0);
1279
+ static VALUE ruby_curl_easy_timeout_ms_get(VALUE self) {
1280
+ ruby_curl_easy *rbce;
1281
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1282
+ return LONG2NUM(rbce->timeout_ms);
1157
1283
  }
1158
1284
 
1159
1285
  /*
@@ -1303,6 +1429,46 @@ static VALUE ruby_curl_easy_low_speed_time_get(VALUE self, VALUE low_speed_time)
1303
1429
  CURB_IMMED_GETTER(ruby_curl_easy, low_speed_time, 0);
1304
1430
  }
1305
1431
 
1432
+ /*
1433
+ * call-seq:
1434
+ * easy.max_send_speed_large = fixnum or nil => fixnum or nil
1435
+ *
1436
+ * Set the maximal sending transfer speed (in bytes per second)
1437
+ */
1438
+ static VALUE ruby_curl_easy_max_send_speed_large_set(VALUE self, VALUE max_send_speed_large) {
1439
+ CURB_IMMED_SETTER(ruby_curl_easy, max_send_speed_large, 0);
1440
+ }
1441
+
1442
+ /*
1443
+ * call-seq:
1444
+ * easy.max_send_speed_large = fixnum or nil => fixnum or nil
1445
+ *
1446
+ * Get the maximal sending transfer speed (in bytes per second)
1447
+ */
1448
+ static VALUE ruby_curl_easy_max_send_speed_large_get(VALUE self, VALUE max_send_speed_large) {
1449
+ CURB_IMMED_GETTER(ruby_curl_easy, max_send_speed_large, 0);
1450
+ }
1451
+
1452
+ /*
1453
+ * call-seq:
1454
+ * easy.max_recv_speed_large = fixnum or nil => fixnum or nil
1455
+ *
1456
+ * Set the maximal receiving transfer speed (in bytes per second)
1457
+ */
1458
+ static VALUE ruby_curl_easy_max_recv_speed_large_set(VALUE self, VALUE max_recv_speed_large) {
1459
+ CURB_IMMED_SETTER(ruby_curl_easy, max_recv_speed_large, 0);
1460
+ }
1461
+
1462
+ /*
1463
+ * call-seq:
1464
+ * easy.max_recv_speed_large = fixnum or nil => fixnum or nil
1465
+ *
1466
+ * Get the maximal receiving transfer speed (in bytes per second)
1467
+ */
1468
+ static VALUE ruby_curl_easy_max_recv_speed_large_get(VALUE self, VALUE max_recv_speed_large) {
1469
+ CURB_IMMED_GETTER(ruby_curl_easy, max_recv_speed_large, 0);
1470
+ }
1471
+
1306
1472
  /*
1307
1473
  * call-seq:
1308
1474
  * easy.username = string => string
@@ -1320,7 +1486,7 @@ static VALUE ruby_curl_easy_username_set(VALUE self, VALUE username) {
1320
1486
  /*
1321
1487
  * call-seq:
1322
1488
  * easy.username => string
1323
- *
1489
+ *
1324
1490
  * Get the current username
1325
1491
  */
1326
1492
  static VALUE ruby_curl_easy_username_get(VALUE self, VALUE username) {
@@ -1348,7 +1514,7 @@ static VALUE ruby_curl_easy_password_set(VALUE self, VALUE password) {
1348
1514
  /*
1349
1515
  * call-seq:
1350
1516
  * easy.password => string
1351
- *
1517
+ *
1352
1518
  * Get the current password
1353
1519
  */
1354
1520
  static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
@@ -1391,7 +1557,7 @@ static VALUE ruby_curl_easy_ssl_version_get(VALUE self, VALUE ssl_version) {
1391
1557
  /*
1392
1558
  * call-seq:
1393
1559
  * easy.use_ssl = value => fixnum or nil
1394
- *
1560
+ *
1395
1561
  * Ensure libcurl uses SSL for FTP connections. Valid options are Curl::CURL_USESSL_NONE,
1396
1562
  * Curl::CURL_USESSL_TRY, Curl::CURL_USESSL_CONTROL, and Curl::CURL_USESSL_ALL.
1397
1563
  */
@@ -1848,7 +2014,7 @@ static VALUE ruby_curl_easy_on_failure_set(int argc, VALUE *argv, VALUE self) {
1848
2014
  * To remove a previously-supplied handler, call this method with no attached
1849
2015
  * block.
1850
2016
  *
1851
- * The +on_missing+ handler is called when request is finished with a
2017
+ * The +on_missing+ handler is called when request is finished with a
1852
2018
  * status of 40x
1853
2019
  */
1854
2020
  static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
@@ -1863,7 +2029,7 @@ static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
1863
2029
  * To remove a previously-supplied handler, call this method with no attached
1864
2030
  * block.
1865
2031
  *
1866
- * The +on_redirect+ handler is called when request is finished with a
2032
+ * The +on_redirect+ handler is called when request is finished with a
1867
2033
  * status of 30x
1868
2034
  */
1869
2035
  static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) {
@@ -1977,6 +2143,38 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
1977
2143
  return header_str;
1978
2144
  }
1979
2145
 
2146
+ /***********************************************
2147
+ * This is an rb_iterate callback used to set up http proxy headers.
2148
+ */
2149
+ static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap) {
2150
+ struct curl_slist **list;
2151
+ VALUE proxy_header_str = Qnil;
2152
+
2153
+ Data_Get_Struct(wrap, struct curl_slist *, list);
2154
+
2155
+ //rb_p(proxy_header);
2156
+
2157
+ if (rb_type(proxy_header) == T_ARRAY) {
2158
+ // we're processing a hash, proxy header is [name, val]
2159
+ VALUE name, value;
2160
+
2161
+ name = rb_obj_as_string(rb_ary_entry(proxy_header, 0));
2162
+ value = rb_obj_as_string(rb_ary_entry(proxy_header, 1));
2163
+
2164
+ // This is a bit inefficient, but we don't want to be modifying
2165
+ // the actual values in the original hash.
2166
+ proxy_header_str = rb_str_plus(name, rb_str_new2(": "));
2167
+ proxy_header_str = rb_str_plus(proxy_header_str, value);
2168
+ } else {
2169
+ proxy_header_str = rb_obj_as_string(proxy_header);
2170
+ }
2171
+
2172
+ //rb_p(header_str);
2173
+
2174
+ *list = curl_slist_append(*list, StringValuePtr(proxy_header_str));
2175
+ return proxy_header_str;
2176
+ }
2177
+
1980
2178
  /***********************************************
1981
2179
  * This is an rb_iterate callback used to set up ftp commands.
1982
2180
  */
@@ -1991,6 +2189,20 @@ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap) {
1991
2189
  return ftp_command_string;
1992
2190
  }
1993
2191
 
2192
+ /***********************************************
2193
+ * This is an rb_iterate callback used to set up the resolve list.
2194
+ */
2195
+ static VALUE cb_each_resolve(VALUE resolve, VALUE wrap) {
2196
+ struct curl_slist **list;
2197
+ VALUE resolve_string;
2198
+ Data_Get_Struct(wrap, struct curl_slist *, list);
2199
+
2200
+ resolve_string = rb_obj_as_string(resolve);
2201
+ *list = curl_slist_append(*list, StringValuePtr(resolve));
2202
+
2203
+ return resolve_string;
2204
+ }
2205
+
1994
2206
  /***********************************************
1995
2207
  *
1996
2208
  * Setup a connection
@@ -2002,7 +2214,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2002
2214
  CURL *curl;
2003
2215
  VALUE url, _url = rb_easy_get("url");
2004
2216
  struct curl_slist **hdrs = &(rbce->curl_headers);
2217
+ struct curl_slist **phdrs = &(rbce->curl_proxy_headers);
2005
2218
  struct curl_slist **cmds = &(rbce->curl_ftp_commands);
2219
+ struct curl_slist **rslv = &(rbce->curl_resolve);
2006
2220
 
2007
2221
  curl = rbce->curl;
2008
2222
 
@@ -2011,7 +2225,6 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2011
2225
  }
2012
2226
 
2013
2227
  url = rb_check_string_type(_url);
2014
-
2015
2228
  curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
2016
2229
 
2017
2230
  // network stuff and auth
@@ -2124,15 +2337,14 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2124
2337
 
2125
2338
  curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, rbce->unrestricted_auth);
2126
2339
 
2127
- /* timeouts override each other we cannot blindly set timeout and timeout_ms */
2128
- if (rbce->timeout && rbce->timeout > 0) {
2129
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, rbce->timeout);
2130
- }
2131
- if (rbce->timeout_ms && rbce->timeout_ms > 0) {
2132
- curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2133
- }
2340
+ #if HAVE_CURLOPT_TIMEOUT_MS
2341
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2342
+ #endif
2343
+
2134
2344
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout);
2345
+ #if HAVE_CURLOPT_CONNECTTIMEOUT_MS
2135
2346
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, rbce->connect_timeout_ms);
2347
+ #endif
2136
2348
  curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout);
2137
2349
 
2138
2350
  curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, rbce->ignore_content_length);
@@ -2151,6 +2363,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2151
2363
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, rbce->low_speed_limit);
2152
2364
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, rbce->low_speed_time);
2153
2365
 
2366
+ curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, rbce->max_recv_speed_large);
2367
+ curl_easy_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, rbce->max_send_speed_large);
2368
+
2154
2369
  // Set up localport / proxy port
2155
2370
  // FIXME these won't get returned to default if they're unset Ruby
2156
2371
  if (rbce->proxy_port > 0) {
@@ -2257,7 +2472,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2257
2472
  rb_warn("libcurl is not configured with SSL support");
2258
2473
  }
2259
2474
  #endif
2260
-
2475
+
2261
2476
  if (rbce->ftp_filemethod > 0) {
2262
2477
  curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, rbce->ftp_filemethod);
2263
2478
  }
@@ -2284,6 +2499,25 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2284
2499
  }
2285
2500
  }
2286
2501
 
2502
+ #if HAVE_CURLOPT_PROXYHEADER
2503
+ /* Setup HTTP proxy headers if necessary */
2504
+ curl_easy_setopt(curl, CURLOPT_PROXYHEADER, NULL); // XXX: maybe we shouldn't be clearing this?
2505
+
2506
+ if (!rb_easy_nil("proxy_headers")) {
2507
+ if (rb_easy_type_check("proxy_headers", T_ARRAY) || rb_easy_type_check("proxy_headers", T_HASH)) {
2508
+ VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, phdrs);
2509
+ rb_iterate(rb_each, rb_easy_get("proxy_headers"), cb_each_http_proxy_header, wrap);
2510
+ } else {
2511
+ VALUE proxy_headers_str = rb_obj_as_string(rb_easy_get("proxy_headers"));
2512
+ *phdrs = curl_slist_append(*hdrs, StringValuePtr(proxy_headers_str));
2513
+ }
2514
+
2515
+ if (*phdrs) {
2516
+ curl_easy_setopt(curl, CURLOPT_PROXYHEADER, *phdrs);
2517
+ }
2518
+ }
2519
+ #endif
2520
+
2287
2521
  /* Setup FTP commands if necessary */
2288
2522
  if (!rb_easy_nil("ftp_commands")) {
2289
2523
  if (rb_easy_type_check("ftp_commands", T_ARRAY)) {
@@ -2296,18 +2530,33 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2296
2530
  }
2297
2531
  }
2298
2532
 
2533
+ #if HAVE_CURLOPT_RESOLVE
2534
+ /* Setup resolve list if necessary */
2535
+ if (!rb_easy_nil("resolve")) {
2536
+ if (rb_easy_type_check("resolve", T_ARRAY)) {
2537
+ VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, rslv);
2538
+ rb_iterate(rb_each, rb_easy_get("resolve"), cb_each_resolve, wrap);
2539
+ }
2540
+
2541
+ if (*rslv) {
2542
+ curl_easy_setopt(curl, CURLOPT_RESOLVE, *rslv);
2543
+ }
2544
+ }
2545
+ #endif
2546
+
2299
2547
  return Qnil;
2300
2548
  }
2301
2549
  /***********************************************
2302
2550
  *
2303
2551
  * Clean up a connection
2304
2552
  *
2305
- * Always returns Qtrue.
2553
+ * Always returns Qnil.
2306
2554
  */
2307
2555
  VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2308
2556
 
2309
2557
  CURL *curl = rbce->curl;
2310
2558
  struct curl_slist *ftp_commands;
2559
+ struct curl_slist *resolve;
2311
2560
 
2312
2561
  /* Free everything up */
2313
2562
  if (rbce->curl_headers) {
@@ -2315,12 +2564,23 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2315
2564
  rbce->curl_headers = NULL;
2316
2565
  }
2317
2566
 
2567
+ if (rbce->curl_proxy_headers) {
2568
+ curl_slist_free_all(rbce->curl_proxy_headers);
2569
+ rbce->curl_proxy_headers = NULL;
2570
+ }
2571
+
2318
2572
  ftp_commands = rbce->curl_ftp_commands;
2319
2573
  if (ftp_commands) {
2320
2574
  curl_slist_free_all(ftp_commands);
2321
2575
  rbce->curl_ftp_commands = NULL;
2322
2576
  }
2323
2577
 
2578
+ resolve = rbce->curl_resolve;
2579
+ if (resolve) {
2580
+ curl_slist_free_all(resolve);
2581
+ rbce->curl_resolve = NULL;
2582
+ }
2583
+
2324
2584
  /* clean up a PUT request's curl options. */
2325
2585
  if (!rb_easy_nil("upload")) {
2326
2586
  rb_easy_del("upload"); // set the upload object to Qnil to let the GC clean up
@@ -2330,6 +2590,9 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2330
2590
  curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0);
2331
2591
  }
2332
2592
 
2593
+ // set values on cleanup to nil
2594
+ rb_easy_del("multi");
2595
+
2333
2596
  return Qnil;
2334
2597
  }
2335
2598
 
@@ -2574,7 +2837,7 @@ static VALUE ruby_curl_easy_response_code_get(VALUE self) {
2574
2837
  static VALUE ruby_curl_easy_primary_ip_get(VALUE self) {
2575
2838
  ruby_curl_easy *rbce;
2576
2839
  char* ip;
2577
-
2840
+
2578
2841
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2579
2842
  curl_easy_getinfo(rbce->curl, CURLINFO_PRIMARY_IP, &ip);
2580
2843
 
@@ -2799,7 +3062,7 @@ static VALUE ruby_curl_easy_redirect_count_get(VALUE self) {
2799
3062
  * call-seq:
2800
3063
  * easy.redirect_url => "http://some.url" or nil
2801
3064
  *
2802
- * Retrieve the URL a redirect would take you to if you
3065
+ * Retrieve the URL a redirect would take you to if you
2803
3066
  * would enable CURLOPT_FOLLOWLOCATION.
2804
3067
  *
2805
3068
  * Requires libcurl 7.18.2 or higher, otherwise -1 is always returned.
@@ -3194,6 +3457,7 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3194
3457
  static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3195
3458
  ruby_curl_easy *rbce;
3196
3459
  long option = NUM2LONG(opt);
3460
+ rb_io_t *open_f_ptr;
3197
3461
 
3198
3462
  Data_Get_Struct(self, ruby_curl_easy, rbce);
3199
3463
 
@@ -3233,6 +3497,9 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3233
3497
  case CURLOPT_NOSIGNAL:
3234
3498
  #if HAVE_CURLOPT_PATH_AS_IS
3235
3499
  case CURLOPT_PATH_AS_IS:
3500
+ #endif
3501
+ #if HAVE_CURLOPT_PIPEWAIT
3502
+ case CURLOPT_PIPEWAIT:
3236
3503
  #endif
3237
3504
  case CURLOPT_HTTPGET:
3238
3505
  case CURLOPT_NOBODY: {
@@ -3301,6 +3568,44 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3301
3568
  curl_easy_setopt(rbce->curl, CURLOPT_UNIX_SOCKET_PATH, StringValueCStr(val));
3302
3569
  } break;
3303
3570
  #endif
3571
+ #if HAVE_CURLOPT_MAX_SEND_SPEED_LARGE
3572
+ case CURLOPT_MAX_SEND_SPEED_LARGE: {
3573
+ curl_easy_setopt(rbce->curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t) NUM2LL(val));
3574
+ } break;
3575
+ #endif
3576
+ #if HAVE_CURLOPT_MAX_RECV_SPEED_LARGE
3577
+ case CURLOPT_MAX_RECV_SPEED_LARGE: {
3578
+ curl_easy_setopt(rbce->curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) NUM2LL(val));
3579
+ } break;
3580
+ #endif
3581
+ #if HAVE_CURLOPT_MAXFILESIZE
3582
+ case CURLOPT_MAXFILESIZE:
3583
+ curl_easy_setopt(rbce->curl, CURLOPT_MAXFILESIZE, NUM2LONG(val));
3584
+ break;
3585
+ #endif
3586
+ #if HAVE_CURLOPT_TCP_KEEPALIVE
3587
+ case CURLOPT_TCP_KEEPALIVE:
3588
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPALIVE, NUM2LONG(val));
3589
+ break;
3590
+ case CURLOPT_TCP_KEEPIDLE:
3591
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPIDLE, NUM2LONG(val));
3592
+ break;
3593
+ case CURLOPT_TCP_KEEPINTVL:
3594
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPINTVL, NUM2LONG(val));
3595
+ break;
3596
+ #endif
3597
+ #if HAVE_CURLOPT_HAPROXYPROTOCOL
3598
+ case CURLOPT_HAPROXYPROTOCOL:
3599
+ curl_easy_setopt(rbce->curl, HAVE_CURLOPT_HAPROXYPROTOCOL, NUM2LONG(val));
3600
+ break;
3601
+ #endif
3602
+ case CURLOPT_STDERR:
3603
+ // libcurl requires raw FILE pointer and this should be IO object in Ruby.
3604
+ // Tempfile or StringIO won't work.
3605
+ Check_Type(val, T_FILE);
3606
+ GetOpenFile(val, open_f_ptr);
3607
+ curl_easy_setopt(rbce->curl, CURLOPT_STDERR, rb_io_stdio_file(open_f_ptr));
3608
+ break;
3304
3609
  default:
3305
3610
  rb_raise(rb_eTypeError, "Curb unsupported option");
3306
3611
  }
@@ -3427,12 +3732,19 @@ void init_curb_easy() {
3427
3732
  cCurlEasy = rb_define_class_under(mCurl, "Easy", rb_cObject);
3428
3733
 
3429
3734
  /* Class methods */
3430
- rb_define_singleton_method(cCurlEasy, "new", ruby_curl_easy_new, -1);
3735
+ rb_define_alloc_func(cCurlEasy, ruby_curl_easy_allocate);
3431
3736
  rb_define_singleton_method(cCurlEasy, "error", ruby_curl_easy_error_message, 1);
3432
3737
 
3738
+ /* Initialize method */
3739
+ rb_define_method(cCurlEasy, "initialize", ruby_curl_easy_initialize, -1);
3740
+
3433
3741
  /* Attributes for config next perform */
3434
3742
  rb_define_method(cCurlEasy, "url", ruby_curl_easy_url_get, 0);
3435
3743
  rb_define_method(cCurlEasy, "proxy_url", ruby_curl_easy_proxy_url_get, 0);
3744
+
3745
+ rb_define_method(cCurlEasy, "proxy_headers=", ruby_curl_easy_proxy_headers_set, 1);
3746
+ rb_define_method(cCurlEasy, "proxy_headers", ruby_curl_easy_proxy_headers_get, 0);
3747
+
3436
3748
  rb_define_method(cCurlEasy, "headers=", ruby_curl_easy_headers_set, 1);
3437
3749
  rb_define_method(cCurlEasy, "headers", ruby_curl_easy_headers_get, 0);
3438
3750
  rb_define_method(cCurlEasy, "interface", ruby_curl_easy_interface_get, 0);
@@ -3459,6 +3771,8 @@ void init_curb_easy() {
3459
3771
  rb_define_method(cCurlEasy, "put_data=", ruby_curl_easy_put_data_set, 1);
3460
3772
  rb_define_method(cCurlEasy, "ftp_commands=", ruby_curl_easy_ftp_commands_set, 1);
3461
3773
  rb_define_method(cCurlEasy, "ftp_commands", ruby_curl_easy_ftp_commands_get, 0);
3774
+ rb_define_method(cCurlEasy, "resolve=", ruby_curl_easy_resolve_set, 1);
3775
+ rb_define_method(cCurlEasy, "resolve", ruby_curl_easy_resolve_get, 0);
3462
3776
 
3463
3777
  rb_define_method(cCurlEasy, "local_port=", ruby_curl_easy_local_port_set, 1);
3464
3778
  rb_define_method(cCurlEasy, "local_port", ruby_curl_easy_local_port_get, 0);
@@ -3490,6 +3804,10 @@ void init_curb_easy() {
3490
3804
  rb_define_method(cCurlEasy, "low_speed_limit", ruby_curl_easy_low_speed_limit_get, 0);
3491
3805
  rb_define_method(cCurlEasy, "low_speed_time=", ruby_curl_easy_low_speed_time_set, 1);
3492
3806
  rb_define_method(cCurlEasy, "low_speed_time", ruby_curl_easy_low_speed_time_get, 0);
3807
+ rb_define_method(cCurlEasy, "max_send_speed_large=", ruby_curl_easy_max_send_speed_large_set, 1);
3808
+ rb_define_method(cCurlEasy, "max_send_speed_large", ruby_curl_easy_max_send_speed_large_get, 0);
3809
+ rb_define_method(cCurlEasy, "max_recv_speed_large=", ruby_curl_easy_max_recv_speed_large_set, 1);
3810
+ rb_define_method(cCurlEasy, "max_recv_speed_large", ruby_curl_easy_max_recv_speed_large_get, 0);
3493
3811
  rb_define_method(cCurlEasy, "ssl_version=", ruby_curl_easy_ssl_version_set, 1);
3494
3812
  rb_define_method(cCurlEasy, "ssl_version", ruby_curl_easy_ssl_version_get, 0);
3495
3813
  rb_define_method(cCurlEasy, "use_ssl=", ruby_curl_easy_use_ssl_set, 1);