curb 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 805549f6e3fe9190d76c507fa67bf6f6e2a6bbe65bde611cb3aeee6c78cad51f
4
- data.tar.gz: 7f6cab9c618e4535e7ff46b48d11bb5f307158d850b95bd560432dfeb9ea3144
3
+ metadata.gz: 26faca47e47561b5ab762a32f58e2771db9a39c2482152fa1116a7082a27de91
4
+ data.tar.gz: a138509513b023f12537a1bff7fa11e6ef563cc5c59af860b73f119705ee1023
5
5
  SHA512:
6
- metadata.gz: 98f411070f4c3ef1646de29052a2c9a37e7aa2c9009102a7f4e483d1d1188d8bbdae50647e6dd4c8c7439120619e22df5f8c90bba6e1a088e2762cc29109c63d
7
- data.tar.gz: bb8ad84d03972d1e42ce61622060019edc4d6286bab9ab459d2fe9e8ccd3cdc04490a8ba58715fd4efa30e3babf82ac96afce3f5b80fddd1d0bd4d47b197a5f1
6
+ metadata.gz: 4686d9ad528ba0f1931c95fa95d25266c8ffcc2214c3a5f527e437f30977f94bde4a48b38d27ad6342cc868c620e2fd4bd214baeae435725a1746d0c93a22aef
7
+ data.tar.gz: 2e4ba49a5906448e0ad381dad6017cf01140166ee78f1b025b9245cc79c6ff20b37ffd18abc61a56289e8bdeee0750a68f3d84369da36d360f7a61775da10566
data/ext/curb.h CHANGED
@@ -28,11 +28,11 @@
28
28
  #include "curb_macros.h"
29
29
 
30
30
  // These should be managed from the Rake 'release' task.
31
- #define CURB_VERSION "1.0.7"
32
- #define CURB_VER_NUM 1007
31
+ #define CURB_VERSION "1.0.8"
32
+ #define CURB_VER_NUM 1008
33
33
  #define CURB_VER_MAJ 1
34
34
  #define CURB_VER_MIN 0
35
- #define CURB_VER_MIC 7
35
+ #define CURB_VER_MIC 8
36
36
  #define CURB_VER_PATCH 0
37
37
 
38
38
 
data/ext/curb_easy.c CHANGED
@@ -2758,6 +2758,88 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2758
2758
  }
2759
2759
  }
2760
2760
 
2761
+ /*
2762
+ * call-seq:
2763
+ * easy.http_patch("url=encoded%20form%20data;and=so%20on") => true
2764
+ * easy.http_patch("url=encoded%20form%20data", "and=so%20on", ...) => true
2765
+ * easy.http_patch("url=encoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
2766
+ * easy.http_patch(Curl::PostField, Curl::PostField ..., Curl::PostField) => true
2767
+ *
2768
+ * PATCH the specified formdata to the currently configured URL using
2769
+ * the current options set for this Curl::Easy instance. This method
2770
+ * always returns true, or raises an exception (defined under
2771
+ * Curl::Err) on error.
2772
+ *
2773
+ * When multipart_form_post is true the multipart logic is used; otherwise,
2774
+ * the arguments are joined into a raw PATCH body.
2775
+ */
2776
+ static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
2777
+ ruby_curl_easy *rbce;
2778
+ CURL *curl;
2779
+ int i;
2780
+ VALUE args_ary;
2781
+
2782
+ rb_scan_args(argc, argv, "*", &args_ary);
2783
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
2784
+ curl = rbce->curl;
2785
+
2786
+ /* Clear the error buffer */
2787
+ memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
2788
+
2789
+ /* Set the custom HTTP method to PATCH */
2790
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
2791
+
2792
+ if (rbce->multipart_form_post) {
2793
+ VALUE ret;
2794
+ struct curl_httppost *first = NULL, *last = NULL;
2795
+
2796
+ /* Build the multipart form (same logic as for POST) */
2797
+ for (i = 0; i < argc; i++) {
2798
+ if (rb_obj_is_instance_of(argv[i], cCurlPostField)) {
2799
+ append_to_form(argv[i], &first, &last);
2800
+ } else if (rb_type(argv[i]) == T_ARRAY) {
2801
+ long j, argv_len = RARRAY_LEN(argv[i]);
2802
+ for (j = 0; j < argv_len; ++j) {
2803
+ if (rb_obj_is_instance_of(rb_ary_entry(argv[i], j), cCurlPostField)) {
2804
+ append_to_form(rb_ary_entry(argv[i], j), &first, &last);
2805
+ } else {
2806
+ rb_raise(eCurlErrInvalidPostField,
2807
+ "You must use PostFields only with multipart form posts");
2808
+ return Qnil;
2809
+ }
2810
+ }
2811
+ } else {
2812
+ rb_raise(eCurlErrInvalidPostField,
2813
+ "You must use PostFields only with multipart form posts");
2814
+ return Qnil;
2815
+ }
2816
+ }
2817
+ /* Disable the POST flag */
2818
+ curl_easy_setopt(curl, CURLOPT_POST, 0);
2819
+ /* Use the built multipart form as the request body */
2820
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
2821
+ ret = rb_funcall(self, rb_intern("perform"), 0);
2822
+ curl_formfree(first);
2823
+ return ret;
2824
+ } else {
2825
+ /* Join arguments into a raw PATCH body */
2826
+ VALUE patch_body = rb_funcall(args_ary, idJoin, 1, rbstrAmp);
2827
+ if (patch_body == Qnil) {
2828
+ rb_raise(eCurlErrError, "Failed to join arguments");
2829
+ return Qnil;
2830
+ } else {
2831
+ if (rb_type(patch_body) == T_STRING && RSTRING_LEN(patch_body) > 0) {
2832
+ ruby_curl_easy_post_body_set(self, patch_body);
2833
+ }
2834
+ /* If postdata_buffer is still nil, set it so that the PATCH header is enabled */
2835
+ if (rb_easy_nil("postdata_buffer")) {
2836
+ ruby_curl_easy_post_body_set(self, patch_body);
2837
+ }
2838
+ return rb_funcall(self, rb_intern("perform"), 0);
2839
+ }
2840
+ }
2841
+ }
2842
+
2761
2843
  /*
2762
2844
  * call-seq:
2763
2845
  * easy.http_put(data) => true
@@ -2766,18 +2848,70 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2766
2848
  * current options set for this Curl::Easy instance. This method always
2767
2849
  * returns true, or raises an exception (defined under Curl::Err) on error.
2768
2850
  */
2769
- static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2851
+ static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
2770
2852
  ruby_curl_easy *rbce;
2771
2853
  CURL *curl;
2854
+ VALUE args_ary;
2855
+ int i;
2772
2856
 
2857
+ rb_scan_args(argc, argv, "*", &args_ary);
2773
2858
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2774
2859
  curl = rbce->curl;
2775
2860
 
2776
2861
  memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
2862
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
2777
2863
 
2778
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2779
- ruby_curl_easy_put_data_set(self, data);
2780
-
2864
+ /* New: if no arguments were provided, treat as an empty PUT */
2865
+ if (RARRAY_LEN(args_ary) == 0) {
2866
+ /* Option 1: explicitly set an empty body */
2867
+ ruby_curl_easy_put_data_set(self, rb_str_new2(""));
2868
+ }
2869
+ /* If a single argument is given and it is a String or responds to read, use legacy behavior */
2870
+ else if (RARRAY_LEN(args_ary) == 1 &&
2871
+ (rb_type(rb_ary_entry(args_ary, 0)) == T_STRING ||
2872
+ rb_respond_to(rb_ary_entry(args_ary, 0), rb_intern("read")))) {
2873
+ ruby_curl_easy_put_data_set(self, rb_ary_entry(args_ary, 0));
2874
+ }
2875
+ /* Otherwise, if multipart_form_post is true, use multipart logic */
2876
+ else if (rbce->multipart_form_post) {
2877
+ VALUE ret;
2878
+ struct curl_httppost *first = NULL, *last = NULL;
2879
+ for (i = 0; i < RARRAY_LEN(args_ary); i++) {
2880
+ VALUE field = rb_ary_entry(args_ary, i);
2881
+ if (rb_obj_is_instance_of(field, cCurlPostField)) {
2882
+ append_to_form(field, &first, &last);
2883
+ } else if (rb_type(field) == T_ARRAY) {
2884
+ long j;
2885
+ for (j = 0; j < RARRAY_LEN(field); j++) {
2886
+ VALUE subfield = rb_ary_entry(field, j);
2887
+ if (rb_obj_is_instance_of(subfield, cCurlPostField)) {
2888
+ append_to_form(subfield, &first, &last);
2889
+ } else {
2890
+ rb_raise(eCurlErrInvalidPostField,
2891
+ "You must use PostFields only with multipart form posts");
2892
+ }
2893
+ }
2894
+ } else {
2895
+ rb_raise(eCurlErrInvalidPostField,
2896
+ "You must use PostFields only with multipart form posts");
2897
+ }
2898
+ }
2899
+ curl_easy_setopt(curl, CURLOPT_POST, 0);
2900
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
2901
+ /* Set the method explicitly to PUT */
2902
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
2903
+ ret = rb_funcall(self, rb_intern("perform"), 0);
2904
+ curl_formfree(first);
2905
+ return ret;
2906
+ }
2907
+ /* Fallback: join all arguments */
2908
+ else {
2909
+ VALUE post_body = rb_funcall(args_ary, idJoin, 1, rbstrAmp);
2910
+ if (post_body != Qnil && rb_type(post_body) == T_STRING &&
2911
+ RSTRING_LEN(post_body) > 0) {
2912
+ ruby_curl_easy_put_data_set(self, post_body);
2913
+ }
2914
+ }
2781
2915
  return rb_funcall(self, rb_intern("perform"), 0);
2782
2916
  }
2783
2917
 
@@ -3946,7 +4080,8 @@ void init_curb_easy() {
3946
4080
 
3947
4081
  rb_define_method(cCurlEasy, "http", ruby_curl_easy_perform_verb, 1);
3948
4082
  rb_define_method(cCurlEasy, "http_post", ruby_curl_easy_perform_post, -1);
3949
- rb_define_method(cCurlEasy, "http_put", ruby_curl_easy_perform_put, 1);
4083
+ rb_define_method(cCurlEasy, "http_put", ruby_curl_easy_perform_put, -1);
4084
+ rb_define_method(cCurlEasy, "http_patch", ruby_curl_easy_perform_patch, -1);
3950
4085
 
3951
4086
  /* Post-perform info methods */
3952
4087
  rb_define_method(cCurlEasy, "body_str", ruby_curl_easy_body_str_get, 0);
data/lib/curl/easy.rb CHANGED
@@ -374,13 +374,36 @@ module Curl
374
374
  #
375
375
  # call-seq:
376
376
  # Curl::Easy.http_put(url, data) {|c| ... }
377
+ # Curl::Easy.http_put(url, "some=urlencoded%20form%20data&and=so%20on") => true
378
+ # Curl::Easy.http_put(url, "some=urlencoded%20form%20data", "and=so%20on", ...) => true
379
+ # Curl::Easy.http_put(url, "some=urlencoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
380
+ # Curl::Easy.http_put(url, Curl::PostField, Curl::PostField ..., Curl::PostField) => true
377
381
  #
378
382
  # see easy.http_put
379
383
  #
380
- def http_put(url, data)
381
- c = Curl::Easy.new url
384
+ def http_put(*args)
385
+ url = args.shift
386
+ c = Curl::Easy.new(url)
387
+ yield c if block_given?
388
+ c.http_put(*args)
389
+ c
390
+ end
391
+
392
+ #
393
+ # call-seq:
394
+ # Curl::Easy.http_patch(url, data) {|c| ... }
395
+ # Curl::Easy.http_patch(url, "some=urlencoded%20form%20data&and=so%20on") => true
396
+ # Curl::Easy.http_patch(url, "some=urlencoded%20form%20data", "and=so%20on", ...) => true
397
+ # Curl::Easy.http_patch(url, "some=urlencoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
398
+ # Curl::Easy.http_patch(url, Curl::PostField, Curl::PostField ..., Curl::PostField) => true
399
+ #
400
+ # see easy.http_patch
401
+ #
402
+ def http_patch(*args)
403
+ url = args.shift
404
+ c = Curl::Easy.new(url)
382
405
  yield c if block_given?
383
- c.http_put data
406
+ c.http_patch(*args)
384
407
  c
385
408
  end
386
409
 
@@ -0,0 +1,32 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+
4
+ require 'curb'
5
+
6
+ class BugIssue102 < Test::Unit::TestCase
7
+
8
+ def test_gc_closewait
9
+ 100.times do
10
+ responses = {}
11
+ requests = ["http://www.google.co.uk/", "http://www.ruby-lang.org/"]
12
+ m = Curl::Multi.new
13
+ # add a few easy handles
14
+ requests.each do |url|
15
+ responses[url] = ""
16
+ c = Curl::Easy.new(url) do|curl|
17
+ curl.follow_location = true
18
+ curl.on_body{|data| responses[url] << data; data.size }
19
+ curl.on_success {|easy| #puts "success, add more easy handles"
20
+ }
21
+ end
22
+ m.add(c)
23
+ end
24
+
25
+ m.perform do
26
+ #puts "idling... can do some work here"
27
+ end
28
+ GC.start
29
+ end
30
+ end
31
+
32
+ end
@@ -765,20 +765,27 @@ class TestCurbCurlEasy < Test::Unit::TestCase
765
765
  assert_equal 'GET', curl.body_str
766
766
  end
767
767
 
768
- def test_post_remote
768
+ def test_remote
769
769
  curl = Curl::Easy.new(TestServlet.url)
770
770
  curl.http_post([Curl::PostField.content('document_id', 5)])
771
771
  assert_equal "POST\ndocument_id=5", curl.unescape(curl.body_str)
772
+
773
+ curl.http_put([Curl::PostField.content('document_id', 5)])
774
+ assert_equal "PUT\ndocument_id=5", curl.unescape(curl.body_str)
775
+
776
+ curl.http_patch([Curl::PostField.content('document_id', 5)])
777
+ assert_equal "PATCH\ndocument_id=5", curl.unescape(curl.body_str)
772
778
  end
773
779
 
774
- def test_post_remote_is_easy_handle
780
+ def test_remote_is_easy_handle
775
781
  # see: http://pastie.org/560852 and
776
782
  # http://groups.google.com/group/curb---ruby-libcurl-bindings/browse_thread/thread/216bb2d9b037f347?hl=en
777
- [:post, :get, :head, :delete].each do |method|
783
+ [:post, :patch, :put, :get, :head, :delete].each do |method|
778
784
  retries = 0
779
785
  begin
780
786
  count = 0
781
- Curl::Easy.send("http_#{method}", TestServlet.url) do|c|
787
+ m = "http_#{method}".to_sym
788
+ Curl::Easy.send(m, TestServlet.url) do|c|
782
789
  count += 1
783
790
  assert_equal Curl::Easy, c.class
784
791
  end
@@ -787,12 +794,14 @@ class TestCurbCurlEasy < Test::Unit::TestCase
787
794
  retries+=1
788
795
  retry if retries < 3
789
796
  raise e
797
+ rescue ArgumentError => e
798
+ assert false, "#{m} - #{e.message}"
790
799
  end
791
800
  end
792
801
  end
793
802
 
794
803
  # see: https://github.com/rvanlieshout/curb/commit/8bcdefddc0162484681ebd1a92d52a642666a445
795
- def test_post_multipart_array_remote
804
+ def test_multipart_array_remote
796
805
  curl = Curl::Easy.new(TestServlet.url)
797
806
  curl.multipart_form_post = true
798
807
  fields = [
@@ -802,9 +811,23 @@ class TestCurbCurlEasy < Test::Unit::TestCase
802
811
  curl.http_post(fields)
803
812
  assert_match(/HTTP POST file upload/, curl.body_str)
804
813
  assert_match(/Content-Disposition: form-data/, curl.body_str)
814
+
815
+ curl = Curl::Easy.new(TestServlet.url)
816
+ curl.multipart_form_post = true
817
+ fields = [
818
+ Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown'))),
819
+ Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown')))
820
+ ]
821
+ curl.http_put(fields)
822
+ assert_match(/HTTP POST file upload/, curl.body_str)
823
+ assert_match(/Content-Disposition: form-data/, curl.body_str)
824
+
825
+ curl.http_patch(fields)
826
+ assert_match(/HTTP POST file upload/, curl.body_str)
827
+ assert_match(/Content-Disposition: form-data/, curl.body_str)
805
828
  end
806
829
 
807
- def test_post_with_body_remote
830
+ def test_with_body_remote
808
831
  curl = Curl::Easy.new(TestServlet.url)
809
832
  curl.post_body = 'foo=bar&encoded%20string=val'
810
833
 
@@ -812,23 +835,42 @@ class TestCurbCurlEasy < Test::Unit::TestCase
812
835
 
813
836
  assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str
814
837
  assert_equal 'foo=bar&encoded%20string=val', curl.post_body
838
+
839
+ curl = Curl::Easy.new(TestServlet.url)
840
+ curl.put_data = 'foo=bar&encoded%20string=val'
841
+
842
+ curl.perform
843
+
844
+ assert_equal "PUT\nfoo=bar&encoded%20string=val", curl.body_str
815
845
  end
816
846
 
817
- def test_form_post_body_remote
847
+ def test_form_body_remote
818
848
  curl = Curl::Easy.new(TestServlet.url)
819
849
  curl.http_post('foo=bar', 'encoded%20string=val')
820
850
 
821
851
  assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str
822
852
  assert_equal 'foo=bar&encoded%20string=val', curl.post_body
823
- end
824
853
 
825
- def test_post_multipart_file_remote
826
854
  curl = Curl::Easy.new(TestServlet.url)
827
- curl.multipart_form_post = true
828
- pf = Curl::PostField.file('readme', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown')))
829
- curl.http_post(pf)
830
- assert_match(/HTTP POST file upload/, curl.body_str)
831
- assert_match(/Content-Disposition: form-data/, curl.body_str)
855
+ curl.http_put('foo=bar', 'encoded%20string=val')
856
+
857
+ assert_equal "PUT\nfoo=bar&encoded%20string=val", curl.body_str
858
+
859
+ curl = Curl::Easy.new(TestServlet.url)
860
+ curl.http_patch('foo=bar', 'encoded%20string=val')
861
+
862
+ assert_equal "PATCH\nfoo=bar&encoded%20string=val", curl.body_str
863
+ end
864
+
865
+ def test_multipart_file_remote
866
+ [:put, :post, :patch].each {|method|
867
+ curl = Curl::Easy.new(TestServlet.url)
868
+ curl.multipart_form_post = true
869
+ pf = Curl::PostField.file('readme', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown')))
870
+ curl.send("http_#{method}", pf)
871
+ assert_match(/HTTP POST file upload/, curl.body_str)
872
+ assert_match(/Content-Disposition: form-data/, curl.body_str)
873
+ }
832
874
  end
833
875
 
834
876
  def test_delete_remote
@@ -1065,6 +1107,12 @@ class TestCurbCurlEasy < Test::Unit::TestCase
1065
1107
  assert_equal "PUT\nhello", curl.body_str
1066
1108
  curl.http('COPY')
1067
1109
  assert_equal 'COPY', curl.body_str
1110
+
1111
+ curl.http_patch
1112
+ assert_equal "PATCH\n", curl.body_str
1113
+
1114
+ curl.http_put
1115
+ assert_equal "PUT\n", curl.body_str
1068
1116
  end
1069
1117
 
1070
1118
  def test_easy_http_verbs_must_respond_to_str
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: curb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ross Bamford
@@ -52,6 +52,7 @@ files:
52
52
  - tests/bug_follow_redirect_288.rb
53
53
  - tests/bug_instance_post_differs_from_class_post.rb
54
54
  - tests/bug_issue102.rb
55
+ - tests/bug_issue277.rb
55
56
  - tests/bug_multi_segfault.rb
56
57
  - tests/bug_postfields_crash.rb
57
58
  - tests/bug_postfields_crash2.rb
@@ -108,6 +109,7 @@ test_files:
108
109
  - tests/bug_follow_redirect_288.rb
109
110
  - tests/bug_instance_post_differs_from_class_post.rb
110
111
  - tests/bug_issue102.rb
112
+ - tests/bug_issue277.rb
111
113
  - tests/bug_multi_segfault.rb
112
114
  - tests/bug_postfields_crash.rb
113
115
  - tests/bug_postfields_crash2.rb