curb 1.2.2 → 1.3.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.
data/ext/curb_easy.c CHANGED
@@ -28,6 +28,14 @@ static VALUE rbstrAmp;
28
28
 
29
29
  VALUE cCurlEasy;
30
30
 
31
+ /* Internal wrapper type for passing pointers through rb_iterate callbacks.
32
+ * No mark/free needed - these are temporary wrappers that don't own memory. */
33
+ static const rb_data_type_t curl_slist_ptr_type = {
34
+ "curl_slist_ptr_wrapper",
35
+ { NULL, NULL, NULL },
36
+ NULL, NULL, 0
37
+ };
38
+
31
39
  // for Ruby 1.8
32
40
  #ifndef HAVE_RB_IO_STDIO_FILE
33
41
  static FILE * rb_io_stdio_file(rb_io_t *fptr) {
@@ -35,6 +43,7 @@ static FILE * rb_io_stdio_file(rb_io_t *fptr) {
35
43
  }
36
44
  #endif
37
45
  static struct curl_slist *duplicate_curl_slist(struct curl_slist *list);
46
+ static size_t proc_data_handler(char *stream, size_t size, size_t nmemb, VALUE proc);
38
47
 
39
48
  /* ================== CURL HANDLER FUNCS ==============*/
40
49
 
@@ -42,6 +51,96 @@ static VALUE callback_exception(VALUE unused, VALUE exception) {
42
51
  return Qfalse;
43
52
  }
44
53
 
54
+ static VALUE callback_exception_store_on_easy(VALUE arg, VALUE exception) {
55
+ ruby_curl_easy *rbce = (ruby_curl_easy *)arg;
56
+
57
+ if (rbce && NIL_P(rbce->callback_error)) {
58
+ rbce->callback_error = exception;
59
+ }
60
+
61
+ return Qfalse;
62
+ }
63
+
64
+ VALUE rb_curl_easy_take_callback_error(ruby_curl_easy *rbce) {
65
+ VALUE exception = Qnil;
66
+
67
+ if (!rbce) {
68
+ return Qnil;
69
+ }
70
+
71
+ exception = rbce->callback_error;
72
+ rbce->callback_error = Qnil;
73
+ return exception;
74
+ }
75
+
76
+ static VALUE ruby_curl_easy_take_callback_error(VALUE self) {
77
+ ruby_curl_easy *rbce = NULL;
78
+
79
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
80
+ return rb_curl_easy_take_callback_error(rbce);
81
+ }
82
+
83
+ static VALUE ensure_clear_easy_callback_active(VALUE arg) {
84
+ ruby_curl_easy *rbce = (ruby_curl_easy *)arg;
85
+ if (rbce) {
86
+ rbce->callback_active = 0;
87
+ }
88
+ return Qnil;
89
+ }
90
+
91
+ static VALUE with_easy_callback_active(ruby_curl_easy *rbce, VALUE (*func)(VALUE), VALUE arg) {
92
+ rbce->callback_active = 1;
93
+ return rb_ensure(func, arg, ensure_clear_easy_callback_active, (VALUE)rbce);
94
+ }
95
+
96
+ struct stream_read_call_args {
97
+ VALUE stream;
98
+ size_t read_bytes;
99
+ };
100
+
101
+ static VALUE call_stream_read(VALUE argp) {
102
+ struct stream_read_call_args *args = (struct stream_read_call_args *)argp;
103
+ return rb_funcall(args->stream, rb_intern("read"), 1, ULONG2NUM((unsigned long)args->read_bytes));
104
+ }
105
+
106
+ static VALUE call_stream_to_s(VALUE stream) {
107
+ return rb_funcall(stream, rb_intern("to_s"), 0);
108
+ }
109
+
110
+ struct stream_seek_call_args {
111
+ VALUE stream;
112
+ curl_off_t offset;
113
+ int origin;
114
+ };
115
+
116
+ static VALUE call_stream_seek(VALUE argp) {
117
+ struct stream_seek_call_args *args = (struct stream_seek_call_args *)argp;
118
+ return rb_funcall(args->stream, rb_intern("seek"), 2, LL2NUM(args->offset), INT2NUM(args->origin));
119
+ }
120
+
121
+ struct proc_data_call_args {
122
+ char *stream;
123
+ size_t size;
124
+ size_t nmemb;
125
+ VALUE proc;
126
+ };
127
+
128
+ static VALUE call_proc_data_handler_wrapped(VALUE argp) {
129
+ struct proc_data_call_args *args = (struct proc_data_call_args *)argp;
130
+ return ULONG2NUM((unsigned long)proc_data_handler(args->stream, args->size, args->nmemb, args->proc));
131
+ }
132
+
133
+ struct easy_callback_dispatch_args {
134
+ ruby_curl_easy *rbce;
135
+ VALUE (*func)(VALUE);
136
+ VALUE arg;
137
+ };
138
+
139
+ static VALUE call_with_easy_callback_active(VALUE argp) {
140
+ struct easy_callback_dispatch_args *args = (struct easy_callback_dispatch_args *)argp;
141
+ return with_easy_callback_active(args->rbce, args->func, args->arg);
142
+ }
143
+
45
144
  /* Default body handler appends to easy.body_data buffer */
46
145
  static size_t default_body_handler(char *stream,
47
146
  size_t size,
@@ -83,8 +182,12 @@ static size_t read_data_handler(void *ptr,
83
182
 
84
183
  if (rb_respond_to(stream, rb_intern("read"))) {//if (rb_respond_to(stream, rb_intern("to_s"))) {
85
184
  /* copy read_bytes from stream into ptr */
86
- VALUE str = rb_funcall(stream, rb_intern("read"), 1, rb_int_new(read_bytes) );
185
+ struct stream_read_call_args args;
186
+ args.stream = stream;
187
+ args.read_bytes = read_bytes;
188
+ VALUE str = with_easy_callback_active(rbce, call_stream_read, (VALUE)&args);
87
189
  if( str != Qnil ) {
190
+ StringValue(str);
88
191
  memcpy(ptr, RSTRING_PTR(str), RSTRING_LEN(str));
89
192
  return RSTRING_LEN(str);
90
193
  }
@@ -98,8 +201,9 @@ static size_t read_data_handler(void *ptr,
98
201
  size_t len;
99
202
  size_t remaining;
100
203
  char *str_ptr;
101
- Data_Get_Struct(upload, ruby_curl_upload, rbcu);
102
- str = rb_funcall(stream, rb_intern("to_s"), 0);
204
+ TypedData_Get_Struct(upload, ruby_curl_upload, &ruby_curl_upload_data_type, rbcu);
205
+ str = with_easy_callback_active(rbce, call_stream_to_s, stream);
206
+ StringValue(str);
103
207
  len = RSTRING_LEN(str);
104
208
  remaining = len - rbcu->offset;
105
209
  str_ptr = RSTRING_PTR(str);
@@ -131,10 +235,14 @@ int seek_data_handler(ruby_curl_easy *rbce,
131
235
  VALUE stream = ruby_curl_upload_stream_get(upload);
132
236
 
133
237
  if (rb_respond_to(stream, rb_intern("seek"))) {
134
- rb_funcall(stream, rb_intern("seek"), 2, SEEK_SET, offset);
238
+ struct stream_seek_call_args args;
239
+ args.stream = stream;
240
+ args.offset = offset;
241
+ args.origin = SEEK_SET;
242
+ with_easy_callback_active(rbce, call_stream_seek, (VALUE)&args);
135
243
  } else {
136
244
  ruby_curl_upload *rbcu;
137
- Data_Get_Struct(upload, ruby_curl_upload, rbcu);
245
+ TypedData_Get_Struct(upload, ruby_curl_upload, &ruby_curl_upload_data_type, rbcu);
138
246
  // This OK because curl only uses SEEK_SET as per the documentation
139
247
  rbcu->offset = offset;
140
248
  }
@@ -166,22 +274,40 @@ static size_t proc_data_handler_body(char *stream,
166
274
  size_t nmemb,
167
275
  ruby_curl_easy *rbce)
168
276
  {
169
- size_t ret;
170
- rbce->callback_active = 1;
171
- ret = proc_data_handler(stream, size, nmemb, rb_easy_get("body_proc"));
172
- rbce->callback_active = 0;
173
- return ret;
277
+ struct proc_data_call_args args;
278
+ struct easy_callback_dispatch_args dispatch_args;
279
+ VALUE procret;
280
+ args.stream = stream;
281
+ args.size = size;
282
+ args.nmemb = nmemb;
283
+ args.proc = rb_easy_get("body_proc");
284
+
285
+ dispatch_args.rbce = rbce;
286
+ dispatch_args.func = call_proc_data_handler_wrapped;
287
+ dispatch_args.arg = (VALUE)&args;
288
+ procret = rb_rescue(call_with_easy_callback_active, (VALUE)&dispatch_args, callback_exception_store_on_easy, (VALUE)rbce);
289
+
290
+ return ((procret == Qfalse) || (procret == Qnil)) ? 0 : NUM2ULONG(procret);
174
291
  }
175
292
  static size_t proc_data_handler_header(char *stream,
176
293
  size_t size,
177
294
  size_t nmemb,
178
295
  ruby_curl_easy *rbce)
179
296
  {
180
- size_t ret;
181
- rbce->callback_active = 1;
182
- ret = proc_data_handler(stream, size, nmemb, rb_easy_get("header_proc"));
183
- rbce->callback_active = 0;
184
- return ret;
297
+ struct proc_data_call_args args;
298
+ struct easy_callback_dispatch_args dispatch_args;
299
+ VALUE procret;
300
+ args.stream = stream;
301
+ args.size = size;
302
+ args.nmemb = nmemb;
303
+ args.proc = rb_easy_get("header_proc");
304
+
305
+ dispatch_args.rbce = rbce;
306
+ dispatch_args.func = call_proc_data_handler_wrapped;
307
+ dispatch_args.arg = (VALUE)&args;
308
+ procret = rb_rescue(call_with_easy_callback_active, (VALUE)&dispatch_args, callback_exception_store_on_easy, (VALUE)rbce);
309
+
310
+ return ((procret == Qfalse) || (procret == Qnil)) ? 0 : NUM2ULONG(procret);
185
311
  }
186
312
 
187
313
 
@@ -193,6 +319,8 @@ static VALUE call_progress_handler(VALUE ary) {
193
319
  rb_ary_entry(ary, 4)); // rb_float_new(ulnow));
194
320
  }
195
321
 
322
+ /* CURLOPT_PROGRESSFUNCTION callback (deprecated since 7.32.0) */
323
+ #ifndef HAVE_CURLOPT_XFERINFOFUNCTION
196
324
  static int proc_progress_handler(void *clientp,
197
325
  double dltotal,
198
326
  double dlnow,
@@ -212,15 +340,46 @@ static int proc_progress_handler(void *clientp,
212
340
  rb_ary_store(callargs, 3, rb_float_new(ultotal));
213
341
  rb_ary_store(callargs, 4, rb_float_new(ulnow));
214
342
 
215
- //v = rb_rescue(range_check, (VALUE)args, range_failed, 0);
216
- //procret = rb_funcall(proc, idCall, 4, rb_float_new(dltotal),
217
- // rb_float_new(dlnow),
218
- // rb_float_new(ultotal),
219
- // rb_float_new(ulnow));
220
- procret = rb_rescue(call_progress_handler, callargs, callback_exception, Qnil);
343
+ struct easy_callback_dispatch_args dispatch_args;
344
+ dispatch_args.rbce = rbce;
345
+ dispatch_args.func = call_progress_handler;
346
+ dispatch_args.arg = callargs;
347
+ procret = rb_rescue(call_with_easy_callback_active, (VALUE)&dispatch_args, callback_exception, Qnil);
348
+
349
+ return(((procret == Qfalse) || (procret == Qnil)) ? -1 : 0);
350
+ }
351
+ #endif
352
+
353
+ /* CURLOPT_XFERINFOFUNCTION callback (since 7.32.0, replaces PROGRESSFUNCTION) */
354
+ #ifdef HAVE_CURLOPT_XFERINFOFUNCTION
355
+ static int proc_xferinfo_handler(void *clientp,
356
+ curl_off_t dltotal,
357
+ curl_off_t dlnow,
358
+ curl_off_t ultotal,
359
+ curl_off_t ulnow) {
360
+ ruby_curl_easy *rbce = (ruby_curl_easy *)clientp;
361
+ VALUE proc = rb_easy_get("progress_proc");
362
+ if (proc == Qnil) {
363
+ return 0;
364
+ }
365
+ VALUE procret;
366
+ VALUE callargs = rb_ary_new2(5);
367
+
368
+ rb_ary_store(callargs, 0, proc);
369
+ rb_ary_store(callargs, 1, LL2NUM(dltotal));
370
+ rb_ary_store(callargs, 2, LL2NUM(dlnow));
371
+ rb_ary_store(callargs, 3, LL2NUM(ultotal));
372
+ rb_ary_store(callargs, 4, LL2NUM(ulnow));
373
+
374
+ struct easy_callback_dispatch_args dispatch_args;
375
+ dispatch_args.rbce = rbce;
376
+ dispatch_args.func = call_progress_handler;
377
+ dispatch_args.arg = callargs;
378
+ procret = rb_rescue(call_with_easy_callback_active, (VALUE)&dispatch_args, callback_exception, Qnil);
221
379
 
222
380
  return(((procret == Qfalse) || (procret == Qnil)) ? -1 : 0);
223
381
  }
382
+ #endif
224
383
 
225
384
  static VALUE call_debug_handler(VALUE ary) {
226
385
  return rb_funcall(rb_ary_entry(ary, 0), idCall, 2,
@@ -241,7 +400,11 @@ static int proc_debug_handler(CURL *curl,
241
400
  rb_ary_store(callargs, 0, proc);
242
401
  rb_ary_store(callargs, 1, INT2NUM(type));
243
402
  rb_ary_store(callargs, 2, rb_str_new(data, data_len));
244
- rb_rescue(call_debug_handler, callargs, callback_exception, Qnil);
403
+ struct easy_callback_dispatch_args dispatch_args;
404
+ dispatch_args.rbce = rbce;
405
+ dispatch_args.func = call_debug_handler;
406
+ dispatch_args.arg = callargs;
407
+ rb_rescue(call_with_easy_callback_active, (VALUE)&dispatch_args, callback_exception, Qnil);
245
408
  /* no way to indicate to libcurl that we should break out given an exception in the on_debug handler...
246
409
  * this means exceptions will be swallowed
247
410
  */
@@ -249,53 +412,121 @@ static int proc_debug_handler(CURL *curl,
249
412
  return 0;
250
413
  }
251
414
 
252
- /* ================== MARK/FREE FUNC ==================*/
253
- void curl_easy_mark(ruby_curl_easy *rbce) {
254
- if (!NIL_P(rbce->opts)) { rb_gc_mark(rbce->opts); }
255
- if (!NIL_P(rbce->multi)) { rb_gc_mark(rbce->multi); }
415
+ /* ================== MARK/FREE/SIZE FUNCS ==================*/
416
+ static void curl_easy_mark(void *ptr) {
417
+ ruby_curl_easy *rbce = (ruby_curl_easy *)ptr;
418
+ if (rbce) {
419
+ if (!NIL_P(rbce->opts)) { rb_gc_mark(rbce->opts); }
420
+ if (!NIL_P(rbce->multi)) { rb_gc_mark(rbce->multi); }
421
+ if (!NIL_P(rbce->callback_error)) { rb_gc_mark(rbce->callback_error); }
422
+ }
256
423
  }
257
424
 
258
- static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
259
- if (!rbce) {
425
+ static void ruby_curl_easy_clear_headers_list(ruby_curl_easy *rbce) {
426
+ if (!rbce || !rbce->curl_headers) {
260
427
  return;
261
428
  }
429
+ if (rbce->curl) {
430
+ curl_easy_setopt(rbce->curl, CURLOPT_HTTPHEADER, NULL);
431
+ }
432
+ curl_slist_free_all(rbce->curl_headers);
433
+ rbce->curl_headers = NULL;
434
+ }
262
435
 
263
- if (!NIL_P(rbce->multi)) {
264
- VALUE multi_val = rbce->multi;
265
- ruby_curl_multi *rbcm = NULL;
436
+ static void ruby_curl_easy_clear_proxy_headers_list(ruby_curl_easy *rbce) {
437
+ if (!rbce || !rbce->curl_proxy_headers) {
438
+ return;
439
+ }
440
+ #ifdef HAVE_CURLOPT_PROXYHEADER
441
+ if (rbce->curl) {
442
+ curl_easy_setopt(rbce->curl, CURLOPT_PROXYHEADER, NULL);
443
+ }
444
+ #endif
445
+ curl_slist_free_all(rbce->curl_proxy_headers);
446
+ rbce->curl_proxy_headers = NULL;
447
+ }
266
448
 
267
- rbce->multi = Qnil;
449
+ static void ruby_curl_easy_clear_ftp_commands_list(ruby_curl_easy *rbce) {
450
+ if (!rbce || !rbce->curl_ftp_commands) {
451
+ return;
452
+ }
453
+ if (rbce->curl) {
454
+ curl_easy_setopt(rbce->curl, CURLOPT_QUOTE, NULL);
455
+ }
456
+ curl_slist_free_all(rbce->curl_ftp_commands);
457
+ rbce->curl_ftp_commands = NULL;
458
+ }
268
459
 
269
- if (!NIL_P(multi_val) && RB_TYPE_P(multi_val, T_DATA)) {
270
- Data_Get_Struct(multi_val, ruby_curl_multi, rbcm);
271
- if (rbcm) {
272
- /* Best-effort: ensure the handle is detached from the multi to
273
- * avoid libcurl retaining a dangling pointer to a soon-to-be
274
- * cleaned-up easy handle. We cannot raise from GC, so ignore errors. */
275
- if (rbcm->handle && rbce->curl) {
276
- curl_multi_remove_handle(rbcm->handle, rbce->curl);
277
- }
278
- rb_curl_multi_forget_easy(rbcm, rbce);
279
- }
280
- }
460
+ static void ruby_curl_easy_clear_resolve_list(ruby_curl_easy *rbce) {
461
+ if (!rbce || !rbce->curl_resolve) {
462
+ return;
463
+ }
464
+ #ifdef HAVE_CURLOPT_RESOLVE
465
+ if (rbce->curl) {
466
+ curl_easy_setopt(rbce->curl, CURLOPT_RESOLVE, NULL);
281
467
  }
468
+ #endif
469
+ curl_slist_free_all(rbce->curl_resolve);
470
+ rbce->curl_resolve = NULL;
471
+ }
282
472
 
283
- if (rbce->curl_headers) {
284
- curl_slist_free_all(rbce->curl_headers);
473
+ /* Legacy wrapper for external callers */
474
+ void ruby_curl_easy_mark(ruby_curl_easy *rbce) {
475
+ curl_easy_mark((void *)rbce);
476
+ }
477
+
478
+ static ruby_curl_multi *ruby_curl_multi_pointer_if_compatible(VALUE multi_val) {
479
+ if (NIL_P(multi_val) || !RB_TYPE_P(multi_val, T_DATA)) {
480
+ return NULL;
481
+ }
482
+
483
+ #if defined(RTYPEDDATA_P) && defined(RTYPEDDATA_TYPE) && defined(RTYPEDDATA_DATA)
484
+ if (!RTYPEDDATA_P(multi_val)) {
485
+ return NULL;
285
486
  }
286
487
 
287
- if (rbce->curl_proxy_headers) {
288
- curl_slist_free_all(rbce->curl_proxy_headers);
488
+ if (RTYPEDDATA_TYPE(multi_val) != &ruby_curl_multi_data_type) {
489
+ return NULL;
490
+ }
491
+
492
+ return (ruby_curl_multi *)RTYPEDDATA_DATA(multi_val);
493
+ #else
494
+ if (!rb_typeddata_is_kind_of(multi_val, &ruby_curl_multi_data_type)) {
495
+ return NULL;
289
496
  }
290
497
 
291
- if (rbce->curl_ftp_commands) {
292
- curl_slist_free_all(rbce->curl_ftp_commands);
498
+ return DATA_PTR(multi_val);
499
+ #endif
500
+ }
501
+
502
+ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
503
+ if (!rbce) {
504
+ return;
293
505
  }
294
506
 
295
- if (rbce->curl_resolve) {
296
- curl_slist_free_all(rbce->curl_resolve);
507
+ if (!NIL_P(rbce->multi)) {
508
+ VALUE multi_val = rbce->multi;
509
+ ruby_curl_multi *rbcm = ruby_curl_multi_pointer_if_compatible(multi_val);
510
+
511
+ rbce->multi = Qnil;
512
+
513
+ if (rbcm) {
514
+ /* Best-effort: ensure the handle is detached from the multi to avoid
515
+ * libcurl retaining a dangling pointer to a soon-to-be cleaned-up easy
516
+ * handle. This path runs during GC, so it must not invoke Ruby APIs that
517
+ * can allocate or raise. */
518
+ if (rbcm->handle && rbce->curl) {
519
+ curl_multi_remove_handle(rbcm->handle, rbce->curl);
520
+ }
521
+ rb_curl_multi_forget_easy(rbcm, rbce);
522
+ }
297
523
  }
298
524
 
525
+ ruby_curl_easy_clear_headers_list(rbce);
526
+ ruby_curl_easy_clear_proxy_headers_list(rbce);
527
+ ruby_curl_easy_clear_ftp_commands_list(rbce);
528
+ ruby_curl_easy_clear_resolve_list(rbce);
529
+
299
530
  if (rbce->curl) {
300
531
  /* disable any progress or debug events */
301
532
  curl_easy_setopt(rbce->curl, CURLOPT_WRITEFUNCTION, NULL);
@@ -305,7 +536,11 @@ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
305
536
  curl_easy_setopt(rbce->curl, CURLOPT_DEBUGFUNCTION, NULL);
306
537
  curl_easy_setopt(rbce->curl, CURLOPT_DEBUGDATA, NULL);
307
538
  curl_easy_setopt(rbce->curl, CURLOPT_VERBOSE, 0);
539
+ #ifdef HAVE_CURLOPT_XFERINFOFUNCTION
540
+ curl_easy_setopt(rbce->curl, CURLOPT_XFERINFOFUNCTION, NULL);
541
+ #else
308
542
  curl_easy_setopt(rbce->curl, CURLOPT_PROGRESSFUNCTION, NULL);
543
+ #endif
309
544
  curl_easy_setopt(rbce->curl, CURLOPT_NOPROGRESS, 1);
310
545
  curl_easy_cleanup(rbce->curl);
311
546
  rbce->curl = NULL;
@@ -314,11 +549,45 @@ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
314
549
  rbce->self = Qnil;
315
550
  }
316
551
 
317
- void curl_easy_free(ruby_curl_easy *rbce) {
318
- ruby_curl_easy_free(rbce);
319
- free(rbce);
552
+ /* TypedData-compatible free function */
553
+ static void curl_easy_free(void *ptr) {
554
+ ruby_curl_easy *rbce = (ruby_curl_easy *)ptr;
555
+ if (rbce) {
556
+ ruby_curl_easy_free(rbce);
557
+ free(rbce);
558
+ }
559
+ }
560
+
561
+ /* Legacy wrapper for external callers (e.g., curb_multi) */
562
+ void ruby_curl_easy_free_wrapper(ruby_curl_easy *rbce) {
563
+ curl_easy_free((void *)rbce);
320
564
  }
321
565
 
566
+ static size_t curl_easy_memsize(const void *ptr) {
567
+ const ruby_curl_easy *rbce = (const ruby_curl_easy *)ptr;
568
+ size_t size = sizeof(ruby_curl_easy);
569
+ /* Note: We don't count curl_slist or CURL handle memory as they're
570
+ * managed by libcurl and would require complex introspection */
571
+ (void)rbce; /* silence unused warning */
572
+ return size;
573
+ }
574
+
575
+ const rb_data_type_t ruby_curl_easy_data_type = {
576
+ "Curl::Easy",
577
+ {
578
+ curl_easy_mark,
579
+ curl_easy_free,
580
+ curl_easy_memsize,
581
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
582
+ NULL, /* compact - not needed */
583
+ #endif
584
+ },
585
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
586
+ NULL, NULL, /* parent, data */
587
+ RUBY_TYPED_FREE_IMMEDIATELY
588
+ #endif
589
+ };
590
+
322
591
 
323
592
  /* ================= ALLOC METHODS ====================*/
324
593
 
@@ -354,6 +623,7 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
354
623
  rbce->ssl_version = -1;
355
624
  rbce->use_ssl = -1;
356
625
  rbce->ftp_filemethod = -1;
626
+ rbce->http_version = CURL_HTTP_VERSION_NONE;
357
627
  rbce->resolve_mode = CURL_IPRESOLVE_WHATEVER;
358
628
 
359
629
  /* bool opts */
@@ -371,6 +641,7 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
371
641
  rbce->cookielist_engine_enabled = 0;
372
642
  rbce->ignore_content_length = 0;
373
643
  rbce->callback_active = 0;
644
+ rbce->callback_error = Qnil;
374
645
  rbce->last_result = 0;
375
646
  }
376
647
 
@@ -387,7 +658,7 @@ static VALUE ruby_curl_easy_allocate(VALUE klass) {
387
658
  rbce->opts = Qnil;
388
659
  rbce->multi = Qnil;
389
660
  ruby_curl_easy_zero(rbce);
390
- return Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
661
+ return TypedData_Wrap_Struct(klass, &ruby_curl_easy_data_type, rbce);
391
662
  }
392
663
 
393
664
  /*
@@ -407,7 +678,7 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
407
678
 
408
679
  rb_scan_args(argc, argv, "01&", &url, &blk);
409
680
 
410
- Data_Get_Struct(self, ruby_curl_easy, rbce);
681
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
411
682
 
412
683
  /* handler */
413
684
  rbce->curl = curl_easy_init();
@@ -465,7 +736,7 @@ static struct curl_slist *duplicate_curl_slist(struct curl_slist *list) {
465
736
  static VALUE ruby_curl_easy_clone(VALUE self) {
466
737
  ruby_curl_easy *rbce, *newrbce;
467
738
 
468
- Data_Get_Struct(self, ruby_curl_easy, rbce);
739
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
469
740
 
470
741
  newrbce = ALLOC(ruby_curl_easy);
471
742
  if (!newrbce) {
@@ -483,6 +754,7 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
483
754
 
484
755
  /* A cloned easy should not retain ownership reference to the original multi. */
485
756
  newrbce->multi = Qnil;
757
+ newrbce->callback_error = Qnil;
486
758
 
487
759
  if (rbce->opts != Qnil) {
488
760
  newrbce->opts = rb_funcall(rbce->opts, rb_intern("dup"), 0);
@@ -491,7 +763,7 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
491
763
  /* Set the error buffer on the new curl handle using the new err_buf */
492
764
  curl_easy_setopt(newrbce->curl, CURLOPT_ERRORBUFFER, newrbce->err_buf);
493
765
 
494
- VALUE clone = Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
766
+ VALUE clone = TypedData_Wrap_Struct(cCurlEasy, &ruby_curl_easy_data_type, newrbce);
495
767
  newrbce->self = clone;
496
768
  curl_easy_setopt(newrbce->curl, CURLOPT_PRIVATE, (void*)newrbce);
497
769
 
@@ -510,7 +782,7 @@ static VALUE ruby_curl_easy_close(VALUE self) {
510
782
  CURLcode ecode;
511
783
  ruby_curl_easy *rbce;
512
784
 
513
- Data_Get_Struct(self, ruby_curl_easy, rbce);
785
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
514
786
 
515
787
  if (rbce->callback_active) {
516
788
  rb_raise(rb_eRuntimeError, "Cannot close an active curl handle within a callback");
@@ -554,7 +826,7 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
554
826
  CURLcode ecode;
555
827
  ruby_curl_easy *rbce;
556
828
  VALUE opts_dup;
557
- Data_Get_Struct(self, ruby_curl_easy, rbce);
829
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
558
830
 
559
831
  if (rbce->callback_active) {
560
832
  rb_raise(rb_eRuntimeError, "Cannot close an active curl handle within a callback");
@@ -562,6 +834,7 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
562
834
 
563
835
  opts_dup = rb_funcall(rbce->opts, rb_intern("dup"), 0);
564
836
 
837
+ ruby_curl_easy_cleanup(self, rbce);
565
838
  curl_easy_reset(rbce->curl);
566
839
  ruby_curl_easy_zero(rbce);
567
840
  rbce->self = self;
@@ -574,18 +847,6 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
574
847
  raise_curl_easy_error_exception(ecode);
575
848
  }
576
849
 
577
- /* Free everything up */
578
- if (rbce->curl_headers) {
579
- curl_slist_free_all(rbce->curl_headers);
580
- rbce->curl_headers = NULL;
581
- }
582
-
583
- /* Free everything up */
584
- if (rbce->curl_proxy_headers) {
585
- curl_slist_free_all(rbce->curl_proxy_headers);
586
- rbce->curl_proxy_headers = NULL;
587
- }
588
-
589
850
  return opts_dup;
590
851
  }
591
852
 
@@ -651,7 +912,7 @@ static VALUE ruby_curl_easy_proxy_headers_set(VALUE self, VALUE proxy_headers) {
651
912
  static VALUE ruby_curl_easy_headers_get(VALUE self) {
652
913
  ruby_curl_easy *rbce;
653
914
  VALUE headers;
654
- Data_Get_Struct(self, ruby_curl_easy, rbce);
915
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
655
916
  headers = rb_easy_get("headers");//rb_hash_aref(rbce->opts, rb_intern("headers"));
656
917
  if (headers == Qnil) { headers = rb_easy_set("headers", rb_hash_new()); }
657
918
  return headers;
@@ -686,7 +947,7 @@ static VALUE ruby_curl_easy_headers_get(VALUE self) {
686
947
  static VALUE ruby_curl_easy_proxy_headers_get(VALUE self) {
687
948
  ruby_curl_easy *rbce;
688
949
  VALUE proxy_headers;
689
- Data_Get_Struct(self, ruby_curl_easy, rbce);
950
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
690
951
  proxy_headers = rb_easy_get("proxy_headers");//rb_hash_aref(rbce->opts, rb_intern("proxy_headers"));
691
952
  if (proxy_headers == Qnil) { proxy_headers = rb_easy_set("proxy_headers", rb_hash_new()); }
692
953
  return proxy_headers;
@@ -917,43 +1178,63 @@ static VALUE ruby_curl_easy_useragent_get(VALUE self) {
917
1178
  *
918
1179
  * This is handy if you want to perform a POST against a Curl::Multi instance.
919
1180
  */
920
- static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
1181
+ static VALUE ruby_curl_easy_post_body_set_with_mode(VALUE self, VALUE post_body, int force_http_get_on_nil) {
921
1182
  ruby_curl_easy *rbce;
922
1183
  CURL *curl;
923
1184
 
1185
+ VALUE body_str;
1186
+ VALUE retained_body_str;
924
1187
  char *data;
925
1188
  long len;
926
1189
 
927
- Data_Get_Struct(self, ruby_curl_easy, rbce);
1190
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
928
1191
 
929
1192
  curl = rbce->curl;
930
1193
 
931
1194
  if ( post_body == Qnil ) {
932
1195
  rb_easy_del("postdata_buffer");
933
- curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
1196
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
1197
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
1198
+ if (force_http_get_on_nil) {
1199
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
1200
+ }
934
1201
 
935
1202
  } else {
936
1203
  if (rb_type(post_body) == T_STRING) {
937
- data = StringValuePtr(post_body);
938
- len = RSTRING_LEN(post_body);
1204
+ body_str = post_body;
939
1205
  }
940
1206
  else if (rb_respond_to(post_body, rb_intern("to_s"))) {
941
- VALUE str_body = rb_funcall(post_body, rb_intern("to_s"), 0);
942
- data = StringValuePtr(str_body);
943
- len = RSTRING_LEN(post_body);
1207
+ body_str = rb_funcall(post_body, rb_intern("to_s"), 0);
944
1208
  }
945
1209
  else {
946
1210
  rb_raise(rb_eRuntimeError, "post data must respond_to .to_s");
947
1211
  }
948
1212
 
1213
+ StringValue(body_str);
1214
+
949
1215
  // Store the string, since it has to hang around for the duration of the
950
1216
  // request. See CURLOPT_POSTFIELDS in the libcurl docs.
951
- //rbce->postdata_buffer = post_body;
952
- rb_easy_set("postdata_buffer", post_body);
1217
+ #ifdef HAVE_CURLOPT_COPYPOSTFIELDS
1218
+ /*
1219
+ * libcurl copies the bytes immediately for COPYPOSTFIELDS, so retain a
1220
+ * matching Ruby snapshot for post_body instead of the caller's mutable
1221
+ * source string.
1222
+ */
1223
+ retained_body_str = rb_str_dup(body_str);
1224
+ #else
1225
+ retained_body_str = body_str;
1226
+ #endif
1227
+ data = StringValuePtr(retained_body_str);
1228
+ len = RSTRING_LEN(retained_body_str);
1229
+ rb_easy_set("postdata_buffer", retained_body_str);
953
1230
 
954
1231
  curl_easy_setopt(curl, CURLOPT_POST, 1);
955
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
956
1232
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
1233
+ #ifdef HAVE_CURLOPT_COPYPOSTFIELDS
1234
+ curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, data);
1235
+ #else
1236
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
1237
+ #endif
957
1238
 
958
1239
  return post_body;
959
1240
  }
@@ -961,6 +1242,10 @@ static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
961
1242
  return Qnil;
962
1243
  }
963
1244
 
1245
+ static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
1246
+ return ruby_curl_easy_post_body_set_with_mode(self, post_body, 1);
1247
+ }
1248
+
964
1249
  /*
965
1250
  * call-seq:
966
1251
  * easy.post_body => string or nil
@@ -985,7 +1270,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
985
1270
  VALUE upload;
986
1271
  VALUE headers;
987
1272
 
988
- Data_Get_Struct(self, ruby_curl_easy, rbce);
1273
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
989
1274
 
990
1275
  upload = ruby_curl_upload_new(cCurlUpload);
991
1276
  ruby_curl_upload_stream_set(upload,data);
@@ -996,13 +1281,16 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
996
1281
  is complete or terminated... */
997
1282
 
998
1283
  curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
1284
+ curl_easy_setopt(curl, CURLOPT_POST, 0);
1285
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
1286
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
999
1287
  curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
1000
1288
  curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
1001
- #if HAVE_CURLOPT_SEEKFUNCTION
1289
+ #ifdef HAVE_CURLOPT_SEEKFUNCTION
1002
1290
  curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
1003
1291
  #endif
1004
1292
  curl_easy_setopt(curl, CURLOPT_READDATA, rbce);
1005
- #if HAVE_CURLOPT_SEEKDATA
1293
+ #ifdef HAVE_CURLOPT_SEEKDATA
1006
1294
  curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
1007
1295
  #endif
1008
1296
 
@@ -1238,7 +1526,7 @@ static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE sel
1238
1526
  long mask = 0;
1239
1527
 
1240
1528
  rb_scan_args(argc, argv, "*", &args_ary);
1241
- Data_Get_Struct(self, ruby_curl_easy, rbce);
1529
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
1242
1530
 
1243
1531
  len = RARRAY_LEN(args_ary);
1244
1532
 
@@ -1343,7 +1631,7 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1343
1631
  */
1344
1632
  static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout_s) {
1345
1633
  ruby_curl_easy *rbce;
1346
- Data_Get_Struct(self, ruby_curl_easy, rbce);
1634
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
1347
1635
 
1348
1636
  if (Qnil == timeout_s || NUM2DBL(timeout_s) <= 0.0) {
1349
1637
  rbce->timeout_ms = 0;
@@ -1366,7 +1654,7 @@ static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout_s) {
1366
1654
  */
1367
1655
  static VALUE ruby_curl_easy_timeout_get(VALUE self) {
1368
1656
  ruby_curl_easy *rbce;
1369
- Data_Get_Struct(self, ruby_curl_easy, rbce);
1657
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
1370
1658
  return DBL2NUM(rbce->timeout_ms / 1000.0);
1371
1659
  }
1372
1660
 
@@ -1384,7 +1672,7 @@ static VALUE ruby_curl_easy_timeout_get(VALUE self) {
1384
1672
  */
1385
1673
  static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1386
1674
  ruby_curl_easy *rbce;
1387
- Data_Get_Struct(self, ruby_curl_easy, rbce);
1675
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
1388
1676
 
1389
1677
  if (Qnil == timeout_ms || NUM2DBL(timeout_ms) <= 0.0) {
1390
1678
  rbce->timeout_ms = 0;
@@ -1404,7 +1692,7 @@ static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1404
1692
  */
1405
1693
  static VALUE ruby_curl_easy_timeout_ms_get(VALUE self) {
1406
1694
  ruby_curl_easy *rbce;
1407
- Data_Get_Struct(self, ruby_curl_easy, rbce);
1695
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
1408
1696
  return LONG2NUM(rbce->timeout_ms);
1409
1697
  }
1410
1698
 
@@ -1602,7 +1890,7 @@ static VALUE ruby_curl_easy_max_recv_speed_large_get(VALUE self) {
1602
1890
  * Set the HTTP Authentication username.
1603
1891
  */
1604
1892
  static VALUE ruby_curl_easy_username_set(VALUE self, VALUE username) {
1605
- #if HAVE_CURLOPT_USERNAME
1893
+ #ifdef HAVE_CURLOPT_USERNAME
1606
1894
  CURB_OBJECT_HSETTER(ruby_curl_easy, username);
1607
1895
  #else
1608
1896
  return Qnil;
@@ -1616,7 +1904,7 @@ static VALUE ruby_curl_easy_username_set(VALUE self, VALUE username) {
1616
1904
  * Get the current username
1617
1905
  */
1618
1906
  static VALUE ruby_curl_easy_username_get(VALUE self) {
1619
- #if HAVE_CURLOPT_USERNAME
1907
+ #ifdef HAVE_CURLOPT_USERNAME
1620
1908
  CURB_OBJECT_HGETTER(ruby_curl_easy, username);
1621
1909
  #else
1622
1910
  return Qnil;
@@ -1630,7 +1918,7 @@ static VALUE ruby_curl_easy_username_get(VALUE self) {
1630
1918
  * Set the HTTP Authentication password.
1631
1919
  */
1632
1920
  static VALUE ruby_curl_easy_password_set(VALUE self, VALUE password) {
1633
- #if HAVE_CURLOPT_PASSWORD
1921
+ #ifdef HAVE_CURLOPT_PASSWORD
1634
1922
  CURB_OBJECT_HSETTER(ruby_curl_easy, password);
1635
1923
  #else
1636
1924
  return Qnil;
@@ -1644,7 +1932,7 @@ static VALUE ruby_curl_easy_password_set(VALUE self, VALUE password) {
1644
1932
  * Get the current password
1645
1933
  */
1646
1934
  static VALUE ruby_curl_easy_password_get(VALUE self) {
1647
- #if HAVE_CURLOPT_PASSWORD
1935
+ #ifdef HAVE_CURLOPT_PASSWORD
1648
1936
  CURB_OBJECT_HGETTER(ruby_curl_easy, password);
1649
1937
  #else
1650
1938
  return Qnil;
@@ -1877,7 +2165,7 @@ static VALUE ruby_curl_easy_use_netrc_q(VALUE self) {
1877
2165
  */
1878
2166
  static VALUE ruby_curl_easy_autoreferer_set(VALUE self, VALUE autoreferer) {
1879
2167
  ruby_curl_easy *rbce;
1880
- Data_Get_Struct(self, ruby_curl_easy, rbce);
2168
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
1881
2169
 
1882
2170
  if (Qtrue == autoreferer) {
1883
2171
  curl_easy_setopt(rbce->curl, CURLOPT_AUTOREFERER, 1);
@@ -2034,7 +2322,7 @@ static VALUE ruby_curl_easy_ignore_content_length_q(VALUE self) {
2034
2322
  static VALUE ruby_curl_easy_resolve_mode(VALUE self) {
2035
2323
  ruby_curl_easy *rbce;
2036
2324
  unsigned short rm;
2037
- Data_Get_Struct(self, ruby_curl_easy, rbce);
2325
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
2038
2326
 
2039
2327
  rm = rbce->resolve_mode;
2040
2328
 
@@ -2066,7 +2354,7 @@ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) {
2066
2354
  } else {
2067
2355
  ruby_curl_easy *rbce;
2068
2356
  ID resolve_mode_id;
2069
- Data_Get_Struct(self, ruby_curl_easy, rbce);
2357
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
2070
2358
 
2071
2359
  resolve_mode_id = rb_to_id(resolve_mode);
2072
2360
 
@@ -2086,6 +2374,46 @@ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) {
2086
2374
  }
2087
2375
  }
2088
2376
 
2377
+ /*
2378
+ * call-seq:
2379
+ * easy.http_version = Curl::HTTP_1_1 => Curl::HTTP_1_1
2380
+ *
2381
+ * Force libcurl to use a specific HTTP protocol version. By default libcurl
2382
+ * negotiates the highest version supported by both peers. Supported constants
2383
+ * include Curl::HTTP_NONE, Curl::HTTP_1_0, Curl::HTTP_1_1, Curl::HTTP_2_0,
2384
+ * Curl::HTTP_2TLS, and Curl::HTTP_2_PRIOR_KNOWLEDGE (when provided by libcurl).
2385
+ */
2386
+ static VALUE ruby_curl_easy_http_version_set(VALUE self, VALUE version) {
2387
+ ruby_curl_easy *rbce;
2388
+ long http_version;
2389
+
2390
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
2391
+
2392
+ if (NIL_P(version)) {
2393
+ http_version = CURL_HTTP_VERSION_NONE;
2394
+ } else {
2395
+ http_version = NUM2LONG(version);
2396
+ }
2397
+
2398
+ rbce->http_version = http_version;
2399
+
2400
+ return version;
2401
+ }
2402
+
2403
+ /*
2404
+ * call-seq:
2405
+ * easy.http_version => integer
2406
+ *
2407
+ * Returns the HTTP protocol version currently configured.
2408
+ */
2409
+ static VALUE ruby_curl_easy_http_version_get(VALUE self) {
2410
+ ruby_curl_easy *rbce;
2411
+
2412
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
2413
+
2414
+ return LONG2NUM(rbce->http_version);
2415
+ }
2416
+
2089
2417
 
2090
2418
  /* ================= EVENT PROCS ================== */
2091
2419
 
@@ -2250,7 +2578,7 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap, int _c, const VALUE *
2250
2578
  struct curl_slist **list;
2251
2579
  VALUE header_str = Qnil;
2252
2580
 
2253
- Data_Get_Struct(wrap, struct curl_slist *, list);
2581
+ TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
2254
2582
 
2255
2583
  //rb_p(header);
2256
2584
 
@@ -2289,7 +2617,7 @@ static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap, int _c, c
2289
2617
  struct curl_slist **list;
2290
2618
  VALUE proxy_header_str = Qnil;
2291
2619
 
2292
- Data_Get_Struct(wrap, struct curl_slist *, list);
2620
+ TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
2293
2621
 
2294
2622
  //rb_p(proxy_header);
2295
2623
 
@@ -2324,7 +2652,7 @@ static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap, int _c, c
2324
2652
  static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) {
2325
2653
  struct curl_slist **list;
2326
2654
  VALUE ftp_command_string;
2327
- Data_Get_Struct(wrap, struct curl_slist *, list);
2655
+ TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
2328
2656
 
2329
2657
  ftp_command_string = rb_obj_as_string(ftp_command);
2330
2658
  struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(ftp_command));
@@ -2342,7 +2670,7 @@ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap, int _c, const VA
2342
2670
  static VALUE cb_each_resolve(VALUE resolve, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) {
2343
2671
  struct curl_slist **list;
2344
2672
  VALUE resolve_string;
2345
- Data_Get_Struct(wrap, struct curl_slist *, list);
2673
+ TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
2346
2674
 
2347
2675
  resolve_string = rb_obj_as_string(resolve);
2348
2676
  struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(resolve));
@@ -2370,6 +2698,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2370
2698
  struct curl_slist **rslv = &(rbce->curl_resolve);
2371
2699
 
2372
2700
  curl = rbce->curl;
2701
+ rbce->callback_error = Qnil;
2373
2702
 
2374
2703
  if (_url == Qnil) {
2375
2704
  rb_raise(eCurlErrError, "No URL supplied");
@@ -2385,7 +2714,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2385
2714
  curl_easy_setopt(curl, CURLOPT_INTERFACE, NULL);
2386
2715
  }
2387
2716
 
2388
- #if HAVE_CURLOPT_USERNAME == 1 && HAVE_CURLOPT_PASSWORD == 1
2717
+ #if defined(HAVE_CURLOPT_USERNAME) && defined(HAVE_CURLOPT_PASSWORD)
2389
2718
  if (!rb_easy_nil("username")) {
2390
2719
  curl_easy_setopt(curl, CURLOPT_USERNAME, rb_easy_get_str("username"));
2391
2720
  } else {
@@ -2401,7 +2730,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2401
2730
 
2402
2731
  if (!rb_easy_nil("userpwd")) {
2403
2732
  curl_easy_setopt(curl, CURLOPT_USERPWD, rb_easy_get_str("userpwd"));
2404
- #if HAVE_CURLOPT_USERNAME == 1
2733
+ #if defined(HAVE_CURLOPT_USERNAME) && defined(HAVE_CURLOPT_PASSWORD)
2405
2734
  } else if (rb_easy_nil("username") && rb_easy_nil("password")) { /* don't set this even to NULL if we have set username and password */
2406
2735
  #else
2407
2736
  } else {
@@ -2421,7 +2750,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2421
2750
  curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, rb_easy_get_str("proxypwd"));
2422
2751
  }
2423
2752
 
2424
- #if HAVE_CURLOPT_NOPROXY
2753
+ #ifdef HAVE_CURLOPT_NOPROXY
2425
2754
  if (rb_easy_nil("noproxy")) {
2426
2755
  curl_easy_setopt(curl, CURLOPT_NOPROXY, NULL);
2427
2756
  } else {
@@ -2459,12 +2788,21 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2459
2788
 
2460
2789
  // progress and debug procs
2461
2790
  if (!rb_easy_nil("progress_proc")) {
2791
+ #ifdef HAVE_CURLOPT_XFERINFOFUNCTION
2792
+ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, &proc_xferinfo_handler);
2793
+ curl_easy_setopt(curl, CURLOPT_XFERINFODATA, rbce);
2794
+ #else
2462
2795
  curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, (curl_progress_callback)&proc_progress_handler);
2463
2796
  curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, rbce);
2797
+ #endif
2464
2798
  curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
2465
2799
  } else {
2466
2800
  curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
2801
+ #ifdef HAVE_CURLOPT_XFERINFOFUNCTION
2802
+ curl_easy_setopt(curl, CURLOPT_XFERINFODATA, rbce);
2803
+ #else
2467
2804
  curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, rbce);
2805
+ #endif
2468
2806
  }
2469
2807
 
2470
2808
  if (!rb_easy_nil("debug_proc")) {
@@ -2504,12 +2842,12 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2504
2842
 
2505
2843
  curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, rbce->unrestricted_auth);
2506
2844
 
2507
- #if HAVE_CURLOPT_TIMEOUT_MS
2845
+ #ifdef HAVE_CURLOPT_TIMEOUT_MS
2508
2846
  curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2509
2847
  #endif
2510
2848
 
2511
2849
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout);
2512
- #if HAVE_CURLOPT_CONNECTTIMEOUT_MS
2850
+ #ifdef HAVE_CURLOPT_CONNECTTIMEOUT_MS
2513
2851
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, rbce->connect_timeout_ms);
2514
2852
  #endif
2515
2853
  curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout);
@@ -2517,6 +2855,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2517
2855
  curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, rbce->ignore_content_length);
2518
2856
 
2519
2857
  curl_easy_setopt(curl, CURLOPT_IPRESOLVE, rbce->resolve_mode);
2858
+ #if HAVE_CURLOPT_HTTP_VERSION
2859
+ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, rbce->http_version);
2860
+ #endif
2520
2861
 
2521
2862
 
2522
2863
  #if LIBCURL_VERSION_NUM >= 0x070a08
@@ -2656,8 +2997,8 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2656
2997
 
2657
2998
  if (!rb_easy_nil("headers")) {
2658
2999
  if (rb_easy_type_check("headers", T_ARRAY) || rb_easy_type_check("headers", T_HASH)) {
2659
- VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, hdrs);
2660
- rb_iterate(rb_each, rb_easy_get("headers"), cb_each_http_header, wrap);
3000
+ VALUE wrap = TypedData_Wrap_Struct(rb_cObject, &curl_slist_ptr_type, hdrs);
3001
+ rb_block_call(rb_easy_get("headers"), rb_intern("each"), 0, NULL, cb_each_http_header, wrap);
2661
3002
  } else {
2662
3003
  VALUE headers_str = rb_obj_as_string(rb_easy_get("headers"));
2663
3004
  struct curl_slist *new_list = curl_slist_append(*hdrs, StringValuePtr(headers_str));
@@ -2672,14 +3013,14 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2672
3013
  }
2673
3014
  }
2674
3015
 
2675
- #if HAVE_CURLOPT_PROXYHEADER
3016
+ #ifdef HAVE_CURLOPT_PROXYHEADER
2676
3017
  /* Setup HTTP proxy headers if necessary */
2677
3018
  curl_easy_setopt(curl, CURLOPT_PROXYHEADER, NULL); // XXX: maybe we shouldn't be clearing this?
2678
3019
 
2679
3020
  if (!rb_easy_nil("proxy_headers")) {
2680
3021
  if (rb_easy_type_check("proxy_headers", T_ARRAY) || rb_easy_type_check("proxy_headers", T_HASH)) {
2681
- VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, phdrs);
2682
- rb_iterate(rb_each, rb_easy_get("proxy_headers"), cb_each_http_proxy_header, wrap);
3022
+ VALUE wrap = TypedData_Wrap_Struct(rb_cObject, &curl_slist_ptr_type, phdrs);
3023
+ rb_block_call(rb_easy_get("proxy_headers"), rb_intern("each"), 0, NULL, cb_each_http_proxy_header, wrap);
2683
3024
  } else {
2684
3025
  VALUE proxy_headers_str = rb_obj_as_string(rb_easy_get("proxy_headers"));
2685
3026
  struct curl_slist *new_list = curl_slist_append(*phdrs, StringValuePtr(proxy_headers_str));
@@ -2698,8 +3039,8 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2698
3039
  /* Setup FTP commands if necessary */
2699
3040
  if (!rb_easy_nil("ftp_commands")) {
2700
3041
  if (rb_easy_type_check("ftp_commands", T_ARRAY)) {
2701
- VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, cmds);
2702
- rb_iterate(rb_each, rb_easy_get("ftp_commands"), cb_each_ftp_command, wrap);
3042
+ VALUE wrap = TypedData_Wrap_Struct(rb_cObject, &curl_slist_ptr_type, cmds);
3043
+ rb_block_call(rb_easy_get("ftp_commands"), rb_intern("each"), 0, NULL, cb_each_ftp_command, wrap);
2703
3044
  }
2704
3045
 
2705
3046
  if (*cmds) {
@@ -2707,12 +3048,12 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2707
3048
  }
2708
3049
  }
2709
3050
 
2710
- #if HAVE_CURLOPT_RESOLVE
3051
+ #ifdef HAVE_CURLOPT_RESOLVE
2711
3052
  /* Setup resolve list if necessary */
2712
3053
  if (!rb_easy_nil("resolve")) {
2713
3054
  if (rb_easy_type_check("resolve", T_ARRAY)) {
2714
- VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, rslv);
2715
- rb_iterate(rb_each, rb_easy_get("resolve"), cb_each_resolve, wrap);
3055
+ VALUE wrap = TypedData_Wrap_Struct(rb_cObject, &curl_slist_ptr_type, rslv);
3056
+ rb_block_call(rb_easy_get("resolve"), rb_intern("each"), 0, NULL, cb_each_resolve, wrap);
2716
3057
  }
2717
3058
 
2718
3059
  if (*rslv) {
@@ -2735,27 +3076,17 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2735
3076
  struct curl_slist *ftp_commands;
2736
3077
  struct curl_slist *resolve;
2737
3078
 
2738
- /* Free everything up */
2739
- if (rbce->curl_headers) {
2740
- curl_slist_free_all(rbce->curl_headers);
2741
- rbce->curl_headers = NULL;
2742
- }
2743
-
2744
- if (rbce->curl_proxy_headers) {
2745
- curl_slist_free_all(rbce->curl_proxy_headers);
2746
- rbce->curl_proxy_headers = NULL;
2747
- }
3079
+ ruby_curl_easy_clear_headers_list(rbce);
3080
+ ruby_curl_easy_clear_proxy_headers_list(rbce);
2748
3081
 
2749
3082
  ftp_commands = rbce->curl_ftp_commands;
2750
3083
  if (ftp_commands) {
2751
- curl_slist_free_all(ftp_commands);
2752
- rbce->curl_ftp_commands = NULL;
3084
+ ruby_curl_easy_clear_ftp_commands_list(rbce);
2753
3085
  }
2754
3086
 
2755
3087
  resolve = rbce->curl_resolve;
2756
3088
  if (resolve) {
2757
- curl_slist_free_all(resolve);
2758
- rbce->curl_resolve = NULL;
3089
+ ruby_curl_easy_clear_resolve_list(rbce);
2759
3090
  }
2760
3091
 
2761
3092
  /* clean up a PUT request's curl options. */
@@ -2781,7 +3112,7 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
2781
3112
  CURL *curl;
2782
3113
  VALUE retval;
2783
3114
 
2784
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3115
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
2785
3116
  curl = rbce->curl;
2786
3117
 
2787
3118
  memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
@@ -2837,6 +3168,27 @@ static VALUE ruby_curl_easy_perform_verb(VALUE self, VALUE verb) {
2837
3168
  }
2838
3169
  }
2839
3170
 
3171
+ static VALUE call_easy_perform(VALUE self) {
3172
+ return rb_funcall(self, rb_intern("perform"), 0);
3173
+ }
3174
+
3175
+ struct easy_form_perform_args {
3176
+ CURL *curl;
3177
+ struct curl_httppost *first;
3178
+ };
3179
+
3180
+ static VALUE ensure_free_form_post(VALUE argp) {
3181
+ struct easy_form_perform_args *args = (struct easy_form_perform_args *)argp;
3182
+ if (args->curl) {
3183
+ curl_easy_setopt(args->curl, CURLOPT_HTTPPOST, NULL);
3184
+ }
3185
+ if (args->first) {
3186
+ curl_formfree(args->first);
3187
+ args->first = NULL;
3188
+ }
3189
+ return Qnil;
3190
+ }
3191
+
2840
3192
  /*
2841
3193
  * call-seq:
2842
3194
  * easy.http_post("url=encoded%20form%20data;and=so%20on") => true
@@ -2869,7 +3221,7 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2869
3221
 
2870
3222
  rb_scan_args(argc, argv, "*", &args_ary);
2871
3223
 
2872
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3224
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
2873
3225
  curl = rbce->curl;
2874
3226
 
2875
3227
  memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
@@ -2903,8 +3255,8 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2903
3255
 
2904
3256
  curl_easy_setopt(curl, CURLOPT_POST, 0);
2905
3257
  curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
2906
- ret = rb_funcall(self, rb_intern("perform"), 0);
2907
- curl_formfree(first);
3258
+ struct easy_form_perform_args perform_args = { curl, first };
3259
+ ret = rb_ensure(call_easy_perform, self, ensure_free_form_post, (VALUE)&perform_args);
2908
3260
 
2909
3261
  return ret;
2910
3262
  } else {
@@ -2952,7 +3304,7 @@ static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
2952
3304
  VALUE args_ary;
2953
3305
 
2954
3306
  rb_scan_args(argc, argv, "*", &args_ary);
2955
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3307
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
2956
3308
  curl = rbce->curl;
2957
3309
 
2958
3310
  /* Clear the error buffer */
@@ -2990,8 +3342,8 @@ static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
2990
3342
  curl_easy_setopt(curl, CURLOPT_POST, 0);
2991
3343
  /* Use the built multipart form as the request body */
2992
3344
  curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
2993
- ret = rb_funcall(self, rb_intern("perform"), 0);
2994
- curl_formfree(first);
3345
+ struct easy_form_perform_args perform_args = { curl, first };
3346
+ ret = rb_ensure(call_easy_perform, self, ensure_free_form_post, (VALUE)&perform_args);
2995
3347
  return ret;
2996
3348
  } else {
2997
3349
  /* Join arguments into a raw PATCH body */
@@ -3027,7 +3379,7 @@ static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
3027
3379
  int i;
3028
3380
 
3029
3381
  rb_scan_args(argc, argv, "*", &args_ary);
3030
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3382
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3031
3383
  curl = rbce->curl;
3032
3384
 
3033
3385
  memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
@@ -3072,8 +3424,8 @@ static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
3072
3424
  curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
3073
3425
  /* Set the method explicitly to PUT */
3074
3426
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
3075
- ret = rb_funcall(self, rb_intern("perform"), 0);
3076
- curl_formfree(first);
3427
+ struct easy_form_perform_args perform_args = { curl, first };
3428
+ ret = rb_ensure(call_easy_perform, self, ensure_free_form_post, (VALUE)&perform_args);
3077
3429
  return ret;
3078
3430
  }
3079
3431
  /* Fallback: join all arguments */
@@ -3133,7 +3485,7 @@ static VALUE ruby_curl_easy_last_effective_url_get(VALUE self) {
3133
3485
  ruby_curl_easy *rbce;
3134
3486
  char* url;
3135
3487
 
3136
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3488
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3137
3489
  curl_easy_getinfo(rbce->curl, CURLINFO_EFFECTIVE_URL, &url);
3138
3490
 
3139
3491
  if (url && url[0]) { // curl returns empty string if none
@@ -3156,7 +3508,7 @@ static VALUE ruby_curl_easy_response_code_get(VALUE self) {
3156
3508
  ruby_curl_easy *rbce;
3157
3509
  long code;
3158
3510
 
3159
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3511
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3160
3512
  #ifdef HAVE_CURLINFO_RESPONSE_CODE
3161
3513
  curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &code);
3162
3514
  #else
@@ -3180,7 +3532,7 @@ static VALUE ruby_curl_easy_primary_ip_get(VALUE self) {
3180
3532
  ruby_curl_easy *rbce;
3181
3533
  char* ip;
3182
3534
 
3183
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3535
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3184
3536
  curl_easy_getinfo(rbce->curl, CURLINFO_PRIMARY_IP, &ip);
3185
3537
 
3186
3538
  if (ip && ip[0]) { // curl returns empty string if none
@@ -3201,7 +3553,7 @@ static VALUE ruby_curl_easy_http_connect_code_get(VALUE self) {
3201
3553
  ruby_curl_easy *rbce;
3202
3554
  long code;
3203
3555
 
3204
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3556
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3205
3557
  curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CONNECTCODE, &code);
3206
3558
 
3207
3559
  return LONG2NUM(code);
@@ -3229,7 +3581,7 @@ static VALUE ruby_curl_easy_file_time_get(VALUE self) {
3229
3581
  ruby_curl_easy *rbce;
3230
3582
  long time;
3231
3583
 
3232
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3584
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3233
3585
  curl_easy_getinfo(rbce->curl, CURLINFO_FILETIME, &time);
3234
3586
 
3235
3587
  return LONG2NUM(time);
@@ -3250,7 +3602,7 @@ static VALUE ruby_curl_easy_total_time_get(VALUE self) {
3250
3602
  ruby_curl_easy *rbce;
3251
3603
  double time;
3252
3604
 
3253
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3605
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3254
3606
  curl_easy_getinfo(rbce->curl, CURLINFO_TOTAL_TIME, &time);
3255
3607
 
3256
3608
  return rb_float_new(time);
@@ -3267,7 +3619,7 @@ static VALUE ruby_curl_easy_name_lookup_time_get(VALUE self) {
3267
3619
  ruby_curl_easy *rbce;
3268
3620
  double time;
3269
3621
 
3270
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3622
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3271
3623
  curl_easy_getinfo(rbce->curl, CURLINFO_NAMELOOKUP_TIME, &time);
3272
3624
 
3273
3625
  return rb_float_new(time);
@@ -3284,7 +3636,7 @@ static VALUE ruby_curl_easy_connect_time_get(VALUE self) {
3284
3636
  ruby_curl_easy *rbce;
3285
3637
  double time;
3286
3638
 
3287
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3639
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3288
3640
  curl_easy_getinfo(rbce->curl, CURLINFO_CONNECT_TIME, &time);
3289
3641
 
3290
3642
  return rb_float_new(time);
@@ -3305,7 +3657,7 @@ static VALUE ruby_curl_easy_app_connect_time_get(VALUE self) {
3305
3657
  ruby_curl_easy *rbce;
3306
3658
  double time;
3307
3659
 
3308
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3660
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3309
3661
  curl_easy_getinfo(rbce->curl, CURLINFO_APPCONNECT_TIME, &time);
3310
3662
 
3311
3663
  return rb_float_new(time);
@@ -3326,7 +3678,7 @@ static VALUE ruby_curl_easy_pre_transfer_time_get(VALUE self) {
3326
3678
  ruby_curl_easy *rbce;
3327
3679
  double time;
3328
3680
 
3329
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3681
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3330
3682
  curl_easy_getinfo(rbce->curl, CURLINFO_PRETRANSFER_TIME, &time);
3331
3683
 
3332
3684
  return rb_float_new(time);
@@ -3344,7 +3696,7 @@ static VALUE ruby_curl_easy_start_transfer_time_get(VALUE self) {
3344
3696
  ruby_curl_easy *rbce;
3345
3697
  double time;
3346
3698
 
3347
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3699
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3348
3700
  curl_easy_getinfo(rbce->curl, CURLINFO_STARTTRANSFER_TIME, &time);
3349
3701
 
3350
3702
  return rb_float_new(time);
@@ -3366,7 +3718,7 @@ static VALUE ruby_curl_easy_redirect_time_get(VALUE self) {
3366
3718
  ruby_curl_easy *rbce;
3367
3719
  double time;
3368
3720
 
3369
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3721
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3370
3722
  curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_TIME, &time);
3371
3723
 
3372
3724
  return rb_float_new(time);
@@ -3389,7 +3741,7 @@ static VALUE ruby_curl_easy_redirect_count_get(VALUE self) {
3389
3741
  ruby_curl_easy *rbce;
3390
3742
  long count;
3391
3743
 
3392
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3744
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3393
3745
  curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_COUNT, &count);
3394
3746
 
3395
3747
  return LONG2NUM(count);
@@ -3414,7 +3766,7 @@ static VALUE ruby_curl_easy_redirect_url_get(VALUE self) {
3414
3766
  ruby_curl_easy *rbce;
3415
3767
  char* url;
3416
3768
 
3417
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3769
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3418
3770
  curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_URL, &url);
3419
3771
 
3420
3772
  if (url && url[0]) { // curl returns empty string if none
@@ -3439,12 +3791,17 @@ static VALUE ruby_curl_easy_redirect_url_get(VALUE self) {
3439
3791
  */
3440
3792
  static VALUE ruby_curl_easy_uploaded_bytes_get(VALUE self) {
3441
3793
  ruby_curl_easy *rbce;
3442
- double bytes;
3794
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3443
3795
 
3444
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3796
+ #ifdef HAVE_CURLINFO_SIZE_UPLOAD_T
3797
+ curl_off_t bytes;
3798
+ curl_easy_getinfo(rbce->curl, CURLINFO_SIZE_UPLOAD_T, &bytes);
3799
+ return LL2NUM(bytes);
3800
+ #else
3801
+ double bytes;
3445
3802
  curl_easy_getinfo(rbce->curl, CURLINFO_SIZE_UPLOAD, &bytes);
3446
-
3447
3803
  return rb_float_new(bytes);
3804
+ #endif
3448
3805
  }
3449
3806
 
3450
3807
  /*
@@ -3456,12 +3813,17 @@ static VALUE ruby_curl_easy_uploaded_bytes_get(VALUE self) {
3456
3813
  */
3457
3814
  static VALUE ruby_curl_easy_downloaded_bytes_get(VALUE self) {
3458
3815
  ruby_curl_easy *rbce;
3459
- double bytes;
3816
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3460
3817
 
3461
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3818
+ #ifdef HAVE_CURLINFO_SIZE_DOWNLOAD_T
3819
+ curl_off_t bytes;
3820
+ curl_easy_getinfo(rbce->curl, CURLINFO_SIZE_DOWNLOAD_T, &bytes);
3821
+ return LL2NUM(bytes);
3822
+ #else
3823
+ double bytes;
3462
3824
  curl_easy_getinfo(rbce->curl, CURLINFO_SIZE_DOWNLOAD, &bytes);
3463
-
3464
3825
  return rb_float_new(bytes);
3826
+ #endif
3465
3827
  }
3466
3828
 
3467
3829
  /*
@@ -3473,12 +3835,17 @@ static VALUE ruby_curl_easy_downloaded_bytes_get(VALUE self) {
3473
3835
  */
3474
3836
  static VALUE ruby_curl_easy_upload_speed_get(VALUE self) {
3475
3837
  ruby_curl_easy *rbce;
3476
- double bytes;
3838
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3477
3839
 
3478
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3840
+ #ifdef HAVE_CURLINFO_SPEED_UPLOAD_T
3841
+ curl_off_t bytes;
3842
+ curl_easy_getinfo(rbce->curl, CURLINFO_SPEED_UPLOAD_T, &bytes);
3843
+ return LL2NUM(bytes);
3844
+ #else
3845
+ double bytes;
3479
3846
  curl_easy_getinfo(rbce->curl, CURLINFO_SPEED_UPLOAD, &bytes);
3480
-
3481
3847
  return rb_float_new(bytes);
3848
+ #endif
3482
3849
  }
3483
3850
 
3484
3851
  /*
@@ -3490,12 +3857,17 @@ static VALUE ruby_curl_easy_upload_speed_get(VALUE self) {
3490
3857
  */
3491
3858
  static VALUE ruby_curl_easy_download_speed_get(VALUE self) {
3492
3859
  ruby_curl_easy *rbce;
3493
- double bytes;
3860
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3494
3861
 
3495
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3862
+ #ifdef HAVE_CURLINFO_SPEED_DOWNLOAD_T
3863
+ curl_off_t bytes;
3864
+ curl_easy_getinfo(rbce->curl, CURLINFO_SPEED_DOWNLOAD_T, &bytes);
3865
+ return LL2NUM(bytes);
3866
+ #else
3867
+ double bytes;
3496
3868
  curl_easy_getinfo(rbce->curl, CURLINFO_SPEED_DOWNLOAD, &bytes);
3497
-
3498
3869
  return rb_float_new(bytes);
3870
+ #endif
3499
3871
  }
3500
3872
 
3501
3873
  /*
@@ -3509,7 +3881,7 @@ static VALUE ruby_curl_easy_header_size_get(VALUE self) {
3509
3881
  ruby_curl_easy *rbce;
3510
3882
  long size;
3511
3883
 
3512
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3884
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3513
3885
  curl_easy_getinfo(rbce->curl, CURLINFO_HEADER_SIZE, &size);
3514
3886
 
3515
3887
  return LONG2NUM(size);
@@ -3527,7 +3899,7 @@ static VALUE ruby_curl_easy_request_size_get(VALUE self) {
3527
3899
  ruby_curl_easy *rbce;
3528
3900
  long size;
3529
3901
 
3530
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3902
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3531
3903
  curl_easy_getinfo(rbce->curl, CURLINFO_REQUEST_SIZE, &size);
3532
3904
 
3533
3905
  return LONG2NUM(size);
@@ -3544,7 +3916,7 @@ static VALUE ruby_curl_easy_ssl_verify_result_get(VALUE self) {
3544
3916
  ruby_curl_easy *rbce;
3545
3917
  long result;
3546
3918
 
3547
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3919
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3548
3920
  curl_easy_getinfo(rbce->curl, CURLINFO_SSL_VERIFYRESULT, &result);
3549
3921
 
3550
3922
  return LONG2NUM(result);
@@ -3567,12 +3939,17 @@ NOTE: you must call curl_slist_free_all(3) on the list pointer once you're done
3567
3939
  */
3568
3940
  static VALUE ruby_curl_easy_downloaded_content_length_get(VALUE self) {
3569
3941
  ruby_curl_easy *rbce;
3570
- double bytes;
3942
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3571
3943
 
3572
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3944
+ #ifdef HAVE_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
3945
+ curl_off_t bytes;
3946
+ curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &bytes);
3947
+ return LL2NUM(bytes);
3948
+ #else
3949
+ double bytes;
3573
3950
  curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &bytes);
3574
-
3575
3951
  return rb_float_new(bytes);
3952
+ #endif
3576
3953
  }
3577
3954
 
3578
3955
  /*
@@ -3583,12 +3960,17 @@ static VALUE ruby_curl_easy_downloaded_content_length_get(VALUE self) {
3583
3960
  */
3584
3961
  static VALUE ruby_curl_easy_uploaded_content_length_get(VALUE self) {
3585
3962
  ruby_curl_easy *rbce;
3586
- double bytes;
3963
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3587
3964
 
3588
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3965
+ #ifdef HAVE_CURLINFO_CONTENT_LENGTH_UPLOAD_T
3966
+ curl_off_t bytes;
3967
+ curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_LENGTH_UPLOAD_T, &bytes);
3968
+ return LL2NUM(bytes);
3969
+ #else
3970
+ double bytes;
3589
3971
  curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &bytes);
3590
-
3591
3972
  return rb_float_new(bytes);
3973
+ #endif
3592
3974
  }
3593
3975
 
3594
3976
  /*
@@ -3604,7 +3986,7 @@ static VALUE ruby_curl_easy_content_type_get(VALUE self) {
3604
3986
  ruby_curl_easy *rbce;
3605
3987
  char* type;
3606
3988
 
3607
- Data_Get_Struct(self, ruby_curl_easy, rbce);
3989
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3608
3990
  curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_TYPE, &type);
3609
3991
 
3610
3992
  if (type && type[0]) { // curl returns empty string if none
@@ -3647,7 +4029,7 @@ static VALUE ruby_curl_easy_os_errno_get(VALUE self) {
3647
4029
  ruby_curl_easy *rbce;
3648
4030
  long result;
3649
4031
 
3650
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4032
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3651
4033
  curl_easy_getinfo(rbce->curl, CURLINFO_OS_ERRNO, &result);
3652
4034
 
3653
4035
  return LONG2NUM(result);
@@ -3676,7 +4058,7 @@ static VALUE ruby_curl_easy_num_connects_get(VALUE self) {
3676
4058
  ruby_curl_easy *rbce;
3677
4059
  long result;
3678
4060
 
3679
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4061
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3680
4062
  curl_easy_getinfo(rbce->curl, CURLINFO_NUM_CONNECTS, &result);
3681
4063
 
3682
4064
  return LONG2NUM(result);
@@ -3713,7 +4095,7 @@ static VALUE ruby_curl_easy_cookielist_get(VALUE self) {
3713
4095
  struct curl_slist *cookie;
3714
4096
  VALUE rb_cookies;
3715
4097
 
3716
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4098
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3717
4099
  curl_easy_getinfo(rbce->curl, CURLINFO_COOKIELIST, &cookies);
3718
4100
  if (!cookies)
3719
4101
  return Qnil;
@@ -3753,7 +4135,7 @@ static VALUE ruby_curl_easy_ftp_entry_path_get(VALUE self) {
3753
4135
  ruby_curl_easy *rbce;
3754
4136
  char* path = NULL;
3755
4137
 
3756
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4138
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3757
4139
  curl_easy_getinfo(rbce->curl, CURLINFO_FTP_ENTRY_PATH, &path);
3758
4140
 
3759
4141
  if (path && path[0]) { // curl returns NULL or empty string if none
@@ -3773,7 +4155,7 @@ static VALUE ruby_curl_easy_ftp_entry_path_get(VALUE self) {
3773
4155
  */
3774
4156
  static VALUE ruby_curl_easy_multi_get(VALUE self) {
3775
4157
  ruby_curl_easy *rbce;
3776
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4158
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3777
4159
  return rbce->multi;
3778
4160
  }
3779
4161
 
@@ -3783,7 +4165,12 @@ static VALUE ruby_curl_easy_multi_get(VALUE self) {
3783
4165
  */
3784
4166
  static VALUE ruby_curl_easy_multi_set(VALUE self, VALUE multi) {
3785
4167
  ruby_curl_easy *rbce;
3786
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4168
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
4169
+
4170
+ if (!NIL_P(multi) && rb_obj_is_kind_of(multi, cCurlMulti) != Qtrue) {
4171
+ rb_raise(rb_eTypeError, "expected Curl::Multi or nil");
4172
+ }
4173
+
3787
4174
  rbce->multi = multi;
3788
4175
  return rbce->multi;
3789
4176
  }
@@ -3794,7 +4181,7 @@ static VALUE ruby_curl_easy_multi_set(VALUE self, VALUE multi) {
3794
4181
  */
3795
4182
  static VALUE ruby_curl_easy_last_result(VALUE self) {
3796
4183
  ruby_curl_easy *rbce;
3797
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4184
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3798
4185
  return LONG2NUM(rbce->last_result);
3799
4186
  }
3800
4187
 
@@ -3804,7 +4191,7 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3804
4191
  */
3805
4192
  static VALUE ruby_curl_easy_last_error(VALUE self) {
3806
4193
  ruby_curl_easy *rbce;
3807
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4194
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3808
4195
 
3809
4196
  if (rbce->err_buf[0]) { // curl returns NULL or empty string if none
3810
4197
  return rb_str_new2(rbce->err_buf);
@@ -3831,7 +4218,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3831
4218
  long option = NUM2LONG(opt);
3832
4219
  rb_io_t *open_f_ptr;
3833
4220
 
3834
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4221
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
3835
4222
 
3836
4223
  switch (option) {
3837
4224
  /* BEHAVIOR OPTIONS */
@@ -3853,9 +4240,11 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3853
4240
  case CURLOPT_CUSTOMREQUEST:
3854
4241
  curl_easy_setopt(rbce->curl, CURLOPT_CUSTOMREQUEST, NIL_P(val) ? NULL : StringValueCStr(val));
3855
4242
  break;
3856
- case CURLOPT_HTTP_VERSION:
3857
- curl_easy_setopt(rbce->curl, CURLOPT_HTTP_VERSION, NUM2LONG(val));
3858
- break;
4243
+ case CURLOPT_HTTP_VERSION: {
4244
+ long http_version = NIL_P(val) ? CURL_HTTP_VERSION_NONE : NUM2LONG(val);
4245
+ rbce->http_version = http_version;
4246
+ curl_easy_setopt(rbce->curl, CURLOPT_HTTP_VERSION, http_version);
4247
+ } break;
3859
4248
  case CURLOPT_PROXY: {
3860
4249
  VALUE proxy_url = val;
3861
4250
  CURB_OBJECT_HSETTER(ruby_curl_easy, proxy_url);
@@ -3867,10 +4256,10 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3867
4256
  case CURLOPT_HEADER:
3868
4257
  case CURLOPT_NOPROGRESS:
3869
4258
  case CURLOPT_NOSIGNAL:
3870
- #if HAVE_CURLOPT_PATH_AS_IS
4259
+ #ifdef HAVE_CURLOPT_PATH_AS_IS
3871
4260
  case CURLOPT_PATH_AS_IS:
3872
4261
  #endif
3873
- #if HAVE_CURLOPT_PIPEWAIT
4262
+ #ifdef HAVE_CURLOPT_PIPEWAIT
3874
4263
  case CURLOPT_PIPEWAIT:
3875
4264
  #endif
3876
4265
  case CURLOPT_HTTPGET:
@@ -3893,7 +4282,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3893
4282
  curl_easy_setopt(rbce->curl, CURLOPT_MAXCONNECTS, NUM2LONG(val));
3894
4283
  } break;
3895
4284
  case CURLOPT_POSTFIELDS: {
3896
- curl_easy_setopt(rbce->curl, CURLOPT_POSTFIELDS, NIL_P(val) ? NULL : StringValueCStr(val));
4285
+ ruby_curl_easy_post_body_set_with_mode(self, val, 0);
3897
4286
  } break;
3898
4287
  case CURLOPT_USERPWD: {
3899
4288
  VALUE userpwd = val;
@@ -3903,7 +4292,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3903
4292
  VALUE proxypwd = val;
3904
4293
  CURB_OBJECT_HSETTER(ruby_curl_easy, proxypwd);
3905
4294
  } break;
3906
- #if HAVE_CURLOPT_NOPROXY
4295
+ #ifdef HAVE_CURLOPT_NOPROXY
3907
4296
  case CURLOPT_NOPROXY: {
3908
4297
  VALUE noproxy = val;
3909
4298
  CURB_OBJECT_HSETTER(ruby_curl_easy, noproxy);
@@ -3921,7 +4310,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3921
4310
  VALUE cookiejar = val;
3922
4311
  CURB_OBJECT_HSETTER(ruby_curl_easy, cookiejar);
3923
4312
  } break;
3924
- #if HAVE_CURLOPT_REQUEST_TARGET
4313
+ #ifdef HAVE_CURLOPT_REQUEST_TARGET
3925
4314
  case CURLOPT_REQUEST_TARGET: {
3926
4315
  /* Forward request-target directly to libcurl as a string. */
3927
4316
  curl_easy_setopt(rbce->curl, CURLOPT_REQUEST_TARGET, NIL_P(val) ? NULL : StringValueCStr(val));
@@ -3931,7 +4320,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3931
4320
  curl_easy_setopt(rbce->curl, CURLOPT_TCP_NODELAY, NUM2LONG(val));
3932
4321
  } break;
3933
4322
  /* FTP-specific toggles */
3934
- #if HAVE_CURLOPT_DIRLISTONLY
4323
+ #ifdef HAVE_CURLOPT_DIRLISTONLY
3935
4324
  case CURLOPT_DIRLISTONLY: {
3936
4325
  int type = rb_type(val);
3937
4326
  VALUE value;
@@ -3945,7 +4334,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3945
4334
  curl_easy_setopt(rbce->curl, CURLOPT_DIRLISTONLY, NUM2LONG(value));
3946
4335
  } break;
3947
4336
  #endif
3948
- #if HAVE_CURLOPT_FTP_USE_EPSV
4337
+ #ifdef HAVE_CURLOPT_FTP_USE_EPSV
3949
4338
  case CURLOPT_FTP_USE_EPSV: {
3950
4339
  int type = rb_type(val);
3951
4340
  VALUE value;
@@ -3959,7 +4348,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3959
4348
  curl_easy_setopt(rbce->curl, CURLOPT_FTP_USE_EPSV, NUM2LONG(value));
3960
4349
  } break;
3961
4350
  #endif
3962
- #if HAVE_CURLOPT_FTP_USE_EPRT
4351
+ #ifdef HAVE_CURLOPT_FTP_USE_EPRT
3963
4352
  case CURLOPT_FTP_USE_EPRT: {
3964
4353
  int type = rb_type(val);
3965
4354
  VALUE value;
@@ -3973,7 +4362,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3973
4362
  curl_easy_setopt(rbce->curl, CURLOPT_FTP_USE_EPRT, NUM2LONG(value));
3974
4363
  } break;
3975
4364
  #endif
3976
- #if HAVE_CURLOPT_FTP_SKIP_PASV_IP
4365
+ #ifdef HAVE_CURLOPT_FTP_SKIP_PASV_IP
3977
4366
  case CURLOPT_FTP_SKIP_PASV_IP: {
3978
4367
  int type = rb_type(val);
3979
4368
  VALUE value;
@@ -4002,32 +4391,32 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
4002
4391
  case CURLOPT_FORBID_REUSE: {
4003
4392
  curl_easy_setopt(rbce->curl, CURLOPT_FORBID_REUSE, NUM2LONG(val));
4004
4393
  } break;
4005
- #if HAVE_CURLOPT_GSSAPI_DELEGATION
4394
+ #ifdef HAVE_CURLOPT_GSSAPI_DELEGATION
4006
4395
  case CURLOPT_GSSAPI_DELEGATION: {
4007
4396
  curl_easy_setopt(rbce->curl, CURLOPT_GSSAPI_DELEGATION, NUM2LONG(val));
4008
4397
  } break;
4009
4398
  #endif
4010
- #if HAVE_CURLOPT_UNIX_SOCKET_PATH
4399
+ #ifdef HAVE_CURLOPT_UNIX_SOCKET_PATH
4011
4400
  case CURLOPT_UNIX_SOCKET_PATH: {
4012
4401
  curl_easy_setopt(rbce->curl, CURLOPT_UNIX_SOCKET_PATH, StringValueCStr(val));
4013
4402
  } break;
4014
4403
  #endif
4015
- #if HAVE_CURLOPT_MAX_SEND_SPEED_LARGE
4404
+ #ifdef HAVE_CURLOPT_MAX_SEND_SPEED_LARGE
4016
4405
  case CURLOPT_MAX_SEND_SPEED_LARGE: {
4017
4406
  curl_easy_setopt(rbce->curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t) NUM2LL(val));
4018
4407
  } break;
4019
4408
  #endif
4020
- #if HAVE_CURLOPT_MAX_RECV_SPEED_LARGE
4409
+ #ifdef HAVE_CURLOPT_MAX_RECV_SPEED_LARGE
4021
4410
  case CURLOPT_MAX_RECV_SPEED_LARGE: {
4022
4411
  curl_easy_setopt(rbce->curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) NUM2LL(val));
4023
4412
  } break;
4024
4413
  #endif
4025
- #if HAVE_CURLOPT_MAXFILESIZE
4414
+ #ifdef HAVE_CURLOPT_MAXFILESIZE
4026
4415
  case CURLOPT_MAXFILESIZE:
4027
4416
  curl_easy_setopt(rbce->curl, CURLOPT_MAXFILESIZE, NUM2LONG(val));
4028
4417
  break;
4029
4418
  #endif
4030
- #if HAVE_CURLOPT_TCP_KEEPALIVE
4419
+ #ifdef HAVE_CURLOPT_TCP_KEEPALIVE
4031
4420
  case CURLOPT_TCP_KEEPALIVE:
4032
4421
  curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPALIVE, NUM2LONG(val));
4033
4422
  break;
@@ -4038,7 +4427,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
4038
4427
  curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPINTVL, NUM2LONG(val));
4039
4428
  break;
4040
4429
  #endif
4041
- #if HAVE_CURLOPT_HAPROXYPROTOCOL
4430
+ #ifdef HAVE_CURLOPT_HAPROXYPROTOCOL
4042
4431
  case CURLOPT_HAPROXYPROTOCOL:
4043
4432
  curl_easy_setopt(rbce->curl, CURLOPT_HAPROXYPROTOCOL, NUM2LONG(val));
4044
4433
  break;
@@ -4057,12 +4446,12 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
4057
4446
  case CURLOPT_REDIR_PROTOCOLS:
4058
4447
  curl_easy_setopt(rbce->curl, option, NUM2LONG(val));
4059
4448
  break;
4060
- #if HAVE_CURLOPT_SSL_SESSIONID_CACHE
4449
+ #ifdef HAVE_CURLOPT_SSL_SESSIONID_CACHE
4061
4450
  case CURLOPT_SSL_SESSIONID_CACHE:
4062
4451
  curl_easy_setopt(rbce->curl, CURLOPT_SSL_SESSIONID_CACHE, NUM2LONG(val));
4063
4452
  break;
4064
4453
  #endif
4065
- #if HAVE_CURLOPT_COOKIELIST
4454
+ #ifdef HAVE_CURLOPT_COOKIELIST
4066
4455
  case CURLOPT_COOKIELIST: {
4067
4456
  /* Forward to libcurl */
4068
4457
  curl_easy_setopt(rbce->curl, CURLOPT_COOKIELIST, StringValueCStr(val));
@@ -4082,14 +4471,15 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
4082
4471
  }
4083
4472
  } break;
4084
4473
  #endif
4085
- #if HAVE_CURLOPT_PROXY_SSL_VERIFYHOST
4474
+ #ifdef HAVE_CURLOPT_PROXY_SSL_VERIFYHOST
4086
4475
  case CURLOPT_PROXY_SSL_VERIFYHOST:
4087
4476
  curl_easy_setopt(rbce->curl, CURLOPT_PROXY_SSL_VERIFYHOST, NUM2LONG(val));
4088
4477
  break;
4089
4478
  #endif
4090
- #if HAVE_CURLOPT_RESOLVE
4479
+ #ifdef HAVE_CURLOPT_RESOLVE
4091
4480
  case CURLOPT_RESOLVE: {
4092
4481
  struct curl_slist *list = NULL;
4482
+ ruby_curl_easy_clear_resolve_list(rbce);
4093
4483
  if (NIL_P(val)) {
4094
4484
  /* When nil is passed, we clear any previous resolve list */
4095
4485
  list = NULL;
@@ -4144,7 +4534,7 @@ static VALUE ruby_curl_easy_get_opt(VALUE self, VALUE opt) {
4144
4534
  static VALUE ruby_curl_easy_inspect(VALUE self) {
4145
4535
  char buf[64];
4146
4536
  ruby_curl_easy *rbce;
4147
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4537
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
4148
4538
  /* if we don't have a url set... we'll crash... */
4149
4539
  if( !rb_easy_nil("url") && rb_easy_type_check("url", T_STRING)) {
4150
4540
  VALUE url = rb_easy_get("url");
@@ -4176,7 +4566,7 @@ static VALUE ruby_curl_easy_escape(VALUE self, VALUE svalue) {
4176
4566
  VALUE rresult;
4177
4567
  VALUE str = svalue;
4178
4568
 
4179
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4569
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
4180
4570
 
4181
4571
  /* NOTE: make sure the value is a string, if not call to_s */
4182
4572
  if( rb_type(str) != T_STRING ) { str = rb_funcall(str,rb_intern("to_s"),0); }
@@ -4207,7 +4597,7 @@ static VALUE ruby_curl_easy_unescape(VALUE self, VALUE str) {
4207
4597
  char *result;
4208
4598
  VALUE rresult;
4209
4599
 
4210
- Data_Get_Struct(self, ruby_curl_easy, rbce);
4600
+ TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
4211
4601
 
4212
4602
  #if (LIBCURL_VERSION_NUM >= 0x070f04)
4213
4603
  result = (char*)curl_easy_unescape(rbce->curl, StringValuePtr(str), (int)RSTRING_LEN(str), &rlen);
@@ -4256,6 +4646,8 @@ void init_curb_easy() {
4256
4646
  /* Attributes for config next perform */
4257
4647
  rb_define_method(cCurlEasy, "url", ruby_curl_easy_url_get, 0);
4258
4648
  rb_define_method(cCurlEasy, "proxy_url", ruby_curl_easy_proxy_url_get, 0);
4649
+ rb_define_method(cCurlEasy, "http_version=", ruby_curl_easy_http_version_set, 1);
4650
+ rb_define_method(cCurlEasy, "http_version", ruby_curl_easy_http_version_get, 0);
4259
4651
 
4260
4652
  rb_define_method(cCurlEasy, "proxy_headers=", ruby_curl_easy_proxy_headers_set, 1);
4261
4653
  rb_define_method(cCurlEasy, "proxy_headers", ruby_curl_easy_proxy_headers_get, 0);
@@ -4429,6 +4821,7 @@ void init_curb_easy() {
4429
4821
 
4430
4822
  rb_define_method(cCurlEasy, "multi", ruby_curl_easy_multi_get, 0);
4431
4823
  rb_define_method(cCurlEasy, "multi=", ruby_curl_easy_multi_set, 1);
4824
+ rb_define_private_method(cCurlEasy, "_take_callback_error", ruby_curl_easy_take_callback_error, 0);
4432
4825
  rb_define_method(cCurlEasy, "last_result", ruby_curl_easy_last_result, 0);
4433
4826
  rb_define_method(cCurlEasy, "last_error", ruby_curl_easy_last_error, 0);
4434
4827