curb 0.5.8.0-x86-darwin-10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of curb might be problematic. Click here for more details.
- data/LICENSE +51 -0
- data/README +157 -0
- data/Rakefile +297 -0
- data/doc.rb +42 -0
- data/ext/curb.c +339 -0
- data/ext/curb.h +50 -0
- data/ext/curb.o +0 -0
- data/ext/curb_core.bundle +0 -0
- data/ext/curb_easy.c +2932 -0
- data/ext/curb_easy.h +103 -0
- data/ext/curb_easy.o +0 -0
- data/ext/curb_errors.c +630 -0
- data/ext/curb_errors.h +128 -0
- data/ext/curb_errors.o +0 -0
- data/ext/curb_macros.h +114 -0
- data/ext/curb_multi.c +487 -0
- data/ext/curb_multi.h +26 -0
- data/ext/curb_multi.o +0 -0
- data/ext/curb_postfield.c +511 -0
- data/ext/curb_postfield.h +40 -0
- data/ext/curb_postfield.o +0 -0
- data/ext/curb_upload.c +80 -0
- data/ext/curb_upload.h +30 -0
- data/ext/curb_upload.o +0 -0
- data/ext/extconf.rb +162 -0
- data/lib/curb.rb +184 -0
- data/lib/curl.rb +2 -0
- data/tests/alltests.rb +3 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +52 -0
- data/tests/bug_instance_post_differs_from_class_post.rb +53 -0
- data/tests/bug_multi_segfault.rb +10 -0
- data/tests/bug_require_last_or_segfault.rb +40 -0
- data/tests/helper.rb +168 -0
- data/tests/require_last_or_segfault_script.rb +36 -0
- data/tests/tc_curl_download.rb +32 -0
- data/tests/tc_curl_easy.rb +664 -0
- data/tests/tc_curl_multi.rb +422 -0
- data/tests/tc_curl_postfield.rb +141 -0
- data/tests/unittests.rb +2 -0
- metadata +96 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
/* curb_postfield.h - Field class for POST method
|
2
|
+
* Copyright (c)2006 Ross Bamford.
|
3
|
+
* Licensed under the Ruby License. See LICENSE for details.
|
4
|
+
*
|
5
|
+
* $Id: curb_postfield.h 4 2006-11-17 18:35:31Z roscopeco $
|
6
|
+
*/
|
7
|
+
#ifndef __CURB_POSTFIELD_H
|
8
|
+
#define __CURB_POSTFIELD_H
|
9
|
+
|
10
|
+
#include "curb.h"
|
11
|
+
|
12
|
+
/*
|
13
|
+
* postfield doesn't actually wrap a curl_httppost - instead,
|
14
|
+
* it just holds together some ruby objects and has a C-side
|
15
|
+
* method to add it to a given form list during the perform.
|
16
|
+
*/
|
17
|
+
typedef struct {
|
18
|
+
/* Objects we associate */
|
19
|
+
VALUE name;
|
20
|
+
VALUE content;
|
21
|
+
VALUE content_type;
|
22
|
+
VALUE content_proc;
|
23
|
+
VALUE local_file;
|
24
|
+
VALUE remote_file;
|
25
|
+
|
26
|
+
/* this will sometimes hold a string, which is the result
|
27
|
+
* of the content_proc invocation. We need it to hang around.
|
28
|
+
*/
|
29
|
+
VALUE buffer_str;
|
30
|
+
} ruby_curl_postfield;
|
31
|
+
|
32
|
+
extern VALUE cCurlPostField;
|
33
|
+
|
34
|
+
void append_to_form(VALUE self,
|
35
|
+
struct curl_httppost **first,
|
36
|
+
struct curl_httppost **last);
|
37
|
+
|
38
|
+
void init_curb_postfield();
|
39
|
+
|
40
|
+
#endif
|
Binary file
|
data/ext/curb_upload.c
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
/* curb_upload.c - Curl upload handle
|
2
|
+
* Copyright (c)2009 Todd A Fisher.
|
3
|
+
* Licensed under the Ruby License. See LICENSE for details.
|
4
|
+
*/
|
5
|
+
#include "curb_upload.h"
|
6
|
+
extern VALUE mCurl;
|
7
|
+
VALUE cCurlUpload;
|
8
|
+
|
9
|
+
#ifdef RDOC_NEVER_DEFINED
|
10
|
+
mCurl = rb_define_module("Curl");
|
11
|
+
#endif
|
12
|
+
|
13
|
+
static void curl_upload_mark(ruby_curl_upload *rbcu) {
|
14
|
+
if (rbcu->stream) rb_gc_mark(rbcu->stream);
|
15
|
+
}
|
16
|
+
static void curl_upload_free(ruby_curl_upload *rbcu) {
|
17
|
+
free(rbcu);
|
18
|
+
}
|
19
|
+
|
20
|
+
/*
|
21
|
+
* call-seq:
|
22
|
+
* internal class for sending large file uploads
|
23
|
+
*/
|
24
|
+
VALUE ruby_curl_upload_new(VALUE klass) {
|
25
|
+
VALUE upload;
|
26
|
+
ruby_curl_upload *rbcu = ALLOC(ruby_curl_upload);
|
27
|
+
rbcu->stream = Qnil;
|
28
|
+
rbcu->offset = 0;
|
29
|
+
upload = Data_Wrap_Struct(klass, curl_upload_mark, curl_upload_free, rbcu);
|
30
|
+
return upload;
|
31
|
+
}
|
32
|
+
|
33
|
+
/*
|
34
|
+
* call-seq:
|
35
|
+
* internal class for sending large file uploads
|
36
|
+
*/
|
37
|
+
VALUE ruby_curl_upload_stream_set(VALUE self, VALUE stream) {
|
38
|
+
ruby_curl_upload *rbcu;
|
39
|
+
Data_Get_Struct(self, ruby_curl_upload, rbcu);
|
40
|
+
rbcu->stream = stream;
|
41
|
+
return stream;
|
42
|
+
}
|
43
|
+
/*
|
44
|
+
* call-seq:
|
45
|
+
* internal class for sending large file uploads
|
46
|
+
*/
|
47
|
+
VALUE ruby_curl_upload_stream_get(VALUE self) {
|
48
|
+
ruby_curl_upload *rbcu;
|
49
|
+
Data_Get_Struct(self, ruby_curl_upload, rbcu);
|
50
|
+
return rbcu->stream;
|
51
|
+
}
|
52
|
+
/*
|
53
|
+
* call-seq:
|
54
|
+
* internal class for sending large file uploads
|
55
|
+
*/
|
56
|
+
VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset) {
|
57
|
+
ruby_curl_upload *rbcu;
|
58
|
+
Data_Get_Struct(self, ruby_curl_upload, rbcu);
|
59
|
+
rbcu->offset = FIX2INT(offset);
|
60
|
+
return offset;
|
61
|
+
}
|
62
|
+
/*
|
63
|
+
* call-seq:
|
64
|
+
* internal class for sending large file uploads
|
65
|
+
*/
|
66
|
+
VALUE ruby_curl_upload_offset_get(VALUE self) {
|
67
|
+
ruby_curl_upload *rbcu;
|
68
|
+
Data_Get_Struct(self, ruby_curl_upload, rbcu);
|
69
|
+
return INT2FIX(rbcu->offset);
|
70
|
+
}
|
71
|
+
|
72
|
+
/* =================== INIT LIB =====================*/
|
73
|
+
void init_curb_upload() {
|
74
|
+
cCurlUpload = rb_define_class_under(mCurl, "Upload", rb_cObject);
|
75
|
+
rb_define_singleton_method(cCurlUpload, "new", ruby_curl_upload_new, 0);
|
76
|
+
rb_define_method(cCurlUpload, "stream=", ruby_curl_upload_stream_set, 1);
|
77
|
+
rb_define_method(cCurlUpload, "stream", ruby_curl_upload_stream_get, 0);
|
78
|
+
rb_define_method(cCurlUpload, "offset=", ruby_curl_upload_offset_set, 1);
|
79
|
+
rb_define_method(cCurlUpload, "offset", ruby_curl_upload_offset_get, 0);
|
80
|
+
}
|
data/ext/curb_upload.h
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
/* curb_upload.h - Curl upload handle
|
2
|
+
* Copyright (c)2009 Todd A Fisher.
|
3
|
+
* Licensed under the Ruby License. See LICENSE for details.
|
4
|
+
*/
|
5
|
+
#ifndef __CURB_UPLOAD_H
|
6
|
+
#define __CURB_UPLOAD_H
|
7
|
+
|
8
|
+
#include "curb.h"
|
9
|
+
|
10
|
+
#include <curl/easy.h>
|
11
|
+
|
12
|
+
/*
|
13
|
+
* Maintain the state of an upload e.g. for putting large streams with very little memory
|
14
|
+
* out to a server. via PUT requests
|
15
|
+
*/
|
16
|
+
typedef struct {
|
17
|
+
VALUE stream;
|
18
|
+
size_t offset;
|
19
|
+
} ruby_curl_upload;
|
20
|
+
|
21
|
+
extern VALUE cCurlUpload;
|
22
|
+
void init_curb_upload();
|
23
|
+
|
24
|
+
VALUE ruby_curl_upload_new(VALUE klass);
|
25
|
+
VALUE ruby_curl_upload_stream_set(VALUE self, VALUE stream);
|
26
|
+
VALUE ruby_curl_upload_stream_get(VALUE self);
|
27
|
+
VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset);
|
28
|
+
VALUE ruby_curl_upload_offset_get(VALUE self);
|
29
|
+
|
30
|
+
#endif
|
data/ext/curb_upload.o
ADDED
Binary file
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
dir_config('curl')
|
4
|
+
|
5
|
+
if find_executable('curl-config')
|
6
|
+
$CFLAGS << " #{`curl-config --cflags`.strip}"
|
7
|
+
if ENV['STATIC_BUILD']
|
8
|
+
$LIBS << " #{`curl-config --static-libs`.strip}"
|
9
|
+
else
|
10
|
+
$LIBS << " #{`curl-config --libs`.strip}"
|
11
|
+
end
|
12
|
+
ca_bundle_path=`curl-config --ca`.strip
|
13
|
+
if !ca_bundle_path.nil? and ca_bundle_path != ''
|
14
|
+
$defs.push( %{-D HAVE_CURL_CONFIG_CA} )
|
15
|
+
$defs.push( %{-D CURL_CONFIG_CA='#{ca_bundle_path.inspect}'} )
|
16
|
+
end
|
17
|
+
elsif !have_library('curl') or !have_header('curl/curl.h')
|
18
|
+
fail <<-EOM
|
19
|
+
Can't find libcurl or curl/curl.h
|
20
|
+
|
21
|
+
Try passing --with-curl-dir or --with-curl-lib and --with-curl-include
|
22
|
+
options to extconf.
|
23
|
+
EOM
|
24
|
+
end
|
25
|
+
|
26
|
+
# Check arch flags
|
27
|
+
archs = $CFLAGS.scan(/-arch\s(.*?)\s/).first # get the first arch flag
|
28
|
+
if archs and archs.size >= 1
|
29
|
+
# need to reduce the number of archs...
|
30
|
+
# guess the first one is correct... at least the first one is probably the ruby installed arch...
|
31
|
+
# this could lead to compiled binaries that crash at runtime...
|
32
|
+
$CFLAGS.gsub!(/-arch\s(.*?)\s/,' ')
|
33
|
+
$CFLAGS << " -arch #{archs.first}"
|
34
|
+
puts "Selected arch: #{archs.first}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def define(s)
|
38
|
+
$defs.push( format("-D HAVE_%s", s.to_s.upcase) )
|
39
|
+
end
|
40
|
+
|
41
|
+
def have_constant(name)
|
42
|
+
checking_for name do
|
43
|
+
src = %{
|
44
|
+
#include <curl/curl.h>
|
45
|
+
int main() {
|
46
|
+
int test = (int)#{name.upcase};
|
47
|
+
return 0;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
if try_compile(src,"#{$CFLAGS} #{$LIBS}")
|
51
|
+
define name
|
52
|
+
true
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
have_constant "curlinfo_redirect_time"
|
60
|
+
have_constant "curlinfo_response_code"
|
61
|
+
have_constant "curlinfo_filetime"
|
62
|
+
have_constant "curlinfo_redirect_count"
|
63
|
+
have_constant "curlinfo_os_errno"
|
64
|
+
have_constant "curlinfo_num_connects"
|
65
|
+
have_constant "curlinfo_ftp_entry_path"
|
66
|
+
have_constant "curl_version_ssl"
|
67
|
+
have_constant "curl_version_libz"
|
68
|
+
have_constant "curl_version_ntlm"
|
69
|
+
have_constant "curl_version_gssnegotiate"
|
70
|
+
have_constant "curl_version_debug"
|
71
|
+
have_constant "curl_version_asynchdns"
|
72
|
+
have_constant "curl_version_spnego"
|
73
|
+
have_constant "curl_version_largefile"
|
74
|
+
have_constant "curl_version_idn"
|
75
|
+
have_constant "curl_version_sspi"
|
76
|
+
have_constant "curl_version_conv"
|
77
|
+
have_constant "curlproxy_http"
|
78
|
+
have_constant "curlproxy_socks4"
|
79
|
+
have_constant "curlproxy_socks5"
|
80
|
+
have_constant "curlauth_basic"
|
81
|
+
have_constant "curlauth_digest"
|
82
|
+
have_constant "curlauth_gssnegotiate"
|
83
|
+
have_constant "curlauth_ntlm"
|
84
|
+
have_constant "curlauth_anysafe"
|
85
|
+
have_constant "curlauth_any"
|
86
|
+
have_constant "curle_tftp_notfound"
|
87
|
+
have_constant "curle_tftp_perm"
|
88
|
+
have_constant "curle_tftp_diskfull"
|
89
|
+
have_constant "curle_tftp_illegal"
|
90
|
+
have_constant "curle_tftp_unknownid"
|
91
|
+
have_constant "curle_tftp_exists"
|
92
|
+
have_constant "curle_tftp_nosuchuser"
|
93
|
+
# older versions of libcurl 7.12
|
94
|
+
have_constant "curle_send_fail_rewind"
|
95
|
+
have_constant "curle_ssl_engine_initfailed"
|
96
|
+
have_constant "curle_login_denied"
|
97
|
+
|
98
|
+
# older than 7.16.3
|
99
|
+
have_constant "curlmopt_maxconnects"
|
100
|
+
|
101
|
+
# additional consts
|
102
|
+
have_constant "curle_conv_failed"
|
103
|
+
have_constant "curle_conv_reqd"
|
104
|
+
have_constant "curle_ssl_cacert_badfile"
|
105
|
+
have_constant "curle_remote_file_not_found"
|
106
|
+
have_constant "curle_ssh"
|
107
|
+
have_constant "curle_ssl_shutdown_failed"
|
108
|
+
have_constant "curle_again"
|
109
|
+
have_constant "curle_ssl_crl_badfile"
|
110
|
+
have_constant "curle_ssl_issuer_error"
|
111
|
+
|
112
|
+
# centos 4.5 build of libcurl
|
113
|
+
have_constant "curlm_bad_socket"
|
114
|
+
have_constant "curlm_unknown_option"
|
115
|
+
have_func("curl_multi_timeout")
|
116
|
+
have_func("curl_multi_fdset")
|
117
|
+
have_func("curl_multi_perform")
|
118
|
+
|
119
|
+
if try_compile('int main() { return 0; }','-Wall')
|
120
|
+
$CFLAGS << ' -Wall'
|
121
|
+
end
|
122
|
+
|
123
|
+
# do some checking to detect ruby 1.8 hash.c vs ruby 1.9 hash.c
|
124
|
+
def test_for(name, const, src)
|
125
|
+
checking_for name do
|
126
|
+
if try_compile(src,"#{$CFLAGS} #{$LIBS}")
|
127
|
+
define const
|
128
|
+
true
|
129
|
+
else
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
test_for("Ruby 1.9 Hash", "RUBY19_HASH", %{
|
135
|
+
#include <ruby.h>
|
136
|
+
int main() {
|
137
|
+
VALUE hash = rb_hash_new();
|
138
|
+
if( RHASH(hash)->ntbl->num_entries ) {
|
139
|
+
return 0;
|
140
|
+
}
|
141
|
+
return 1;
|
142
|
+
}
|
143
|
+
})
|
144
|
+
test_for("Ruby 1.9 st.h", "RUBY19_ST_H", %{
|
145
|
+
#include <ruby.h>
|
146
|
+
#include <ruby/st.h>
|
147
|
+
int main() {
|
148
|
+
return 0;
|
149
|
+
}
|
150
|
+
})
|
151
|
+
|
152
|
+
test_for("curl_easy_escape", "CURL_EASY_ESCAPE", %{
|
153
|
+
#include <curl/curl.h>
|
154
|
+
int main() {
|
155
|
+
CURL *easy = curl_easy_init();
|
156
|
+
curl_easy_escape(easy,"hello",5);
|
157
|
+
return 0;
|
158
|
+
}
|
159
|
+
})
|
160
|
+
|
161
|
+
create_header('curb_config.h')
|
162
|
+
create_makefile('curb_core')
|
data/lib/curb.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'curb_core'
|
2
|
+
|
3
|
+
module Curl
|
4
|
+
class Easy
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# call-seq:
|
8
|
+
# Curl::Easy.download(url, filename = url.split(/\?/).first.split(/\//).last) { |curl| ... }
|
9
|
+
#
|
10
|
+
# Stream the specified url (via perform) and save the data directly to the
|
11
|
+
# supplied filename (defaults to the last component of the URL path, which will
|
12
|
+
# usually be the filename most simple urls).
|
13
|
+
#
|
14
|
+
# If a block is supplied, it will be passed the curl instance prior to the
|
15
|
+
# perform call.
|
16
|
+
#
|
17
|
+
# *Note* that the semantics of the on_body handler are subtly changed when using
|
18
|
+
# download, to account for the automatic routing of data to the specified file: The
|
19
|
+
# data string is passed to the handler *before* it is written
|
20
|
+
# to the file, allowing the handler to perform mutative operations where
|
21
|
+
# necessary. As usual, the transfer will be aborted if the on_body handler
|
22
|
+
# returns a size that differs from the data chunk size - in this case, the
|
23
|
+
# offending chunk will *not* be written to the file, the file will be closed,
|
24
|
+
# and a Curl::Err::AbortedByCallbackError will be raised.
|
25
|
+
def download(url, filename = url.split(/\?/).first.split(/\//).last, &blk)
|
26
|
+
curl = Curl::Easy.new(url, &blk)
|
27
|
+
|
28
|
+
File.open(filename, "wb") do |output|
|
29
|
+
old_on_body = curl.on_body do |data|
|
30
|
+
result = old_on_body ? old_on_body.call(data) : data.length
|
31
|
+
output << data if result == data.length
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
curl.perform
|
36
|
+
end
|
37
|
+
|
38
|
+
return curl
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Allow the incoming cert string to be file:password
|
43
|
+
# but be careful to not use a colon from a windows file path
|
44
|
+
# as the split point. Mimic what curl's main does
|
45
|
+
alias_method :native_cert=, :cert=
|
46
|
+
def cert=(cert_file)
|
47
|
+
pos = cert_file.rindex(':')
|
48
|
+
if pos && pos > 1
|
49
|
+
self.native_cert= cert_file[0..pos-1]
|
50
|
+
self.certpassword= cert_file[pos+1..-1]
|
51
|
+
else
|
52
|
+
self.native_cert= cert_file
|
53
|
+
end
|
54
|
+
self.cert
|
55
|
+
end
|
56
|
+
end
|
57
|
+
class Multi
|
58
|
+
class << self
|
59
|
+
# call-seq:
|
60
|
+
# Curl::Multi.get('url1','url2','url3','url4','url5', :follow_location => true) do|easy|
|
61
|
+
# easy
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# Blocking call to fetch multiple url's in parallel.
|
65
|
+
def get(urls, easy_options={}, multi_options={}, &blk)
|
66
|
+
url_confs = []
|
67
|
+
urls.each do|url|
|
68
|
+
url_confs << {:url => url, :method => :get}.merge(easy_options)
|
69
|
+
end
|
70
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# call-seq:
|
74
|
+
#
|
75
|
+
# Curl::Multi.post([{:url => 'url1', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
|
76
|
+
# {:url => 'url2', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}},
|
77
|
+
# {:url => 'url3', :post_fields => {'field1' => 'value1', 'field2' => 'value2'}}],
|
78
|
+
# { :follow_location => true, :multipart_form_post => true },
|
79
|
+
# {:pipeline => true }) do|easy|
|
80
|
+
# easy_handle_on_request_complete
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# Blocking call to POST multiple form's in parallel.
|
84
|
+
#
|
85
|
+
# urls_with_config: is a hash of url's pointing to the postfields to send
|
86
|
+
# easy_options: are a set of common options to set on all easy handles
|
87
|
+
# multi_options: options to set on the Curl::Multi handle
|
88
|
+
#
|
89
|
+
def post(urls_with_config, easy_options={}, multi_options={}, &blk)
|
90
|
+
url_confs = []
|
91
|
+
urls_with_config.each do|uconf|
|
92
|
+
url_confs << uconf.merge(:method => :post).merge(easy_options)
|
93
|
+
end
|
94
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
|
95
|
+
end
|
96
|
+
|
97
|
+
# call-seq:
|
98
|
+
#
|
99
|
+
# Curl::Multi.put([{:url => 'url1', :put_data => "some message"},
|
100
|
+
# {:url => 'url2', :put_data => IO.read('filepath')},
|
101
|
+
# {:url => 'url3', :put_data => "maybe another string or socket?"],
|
102
|
+
# {:follow_location => true},
|
103
|
+
# {:pipeline => true }) do|easy|
|
104
|
+
# easy_handle_on_request_complete
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# Blocking call to POST multiple form's in parallel.
|
108
|
+
#
|
109
|
+
# urls_with_config: is a hash of url's pointing to the postfields to send
|
110
|
+
# easy_options: are a set of common options to set on all easy handles
|
111
|
+
# multi_options: options to set on the Curl::Multi handle
|
112
|
+
#
|
113
|
+
def put(urls_with_config, easy_options={}, multi_options={}, &blk)
|
114
|
+
url_confs = []
|
115
|
+
urls_with_config.each do|uconf|
|
116
|
+
url_confs << uconf.merge(:method => :put).merge(easy_options)
|
117
|
+
end
|
118
|
+
self.http(url_confs, multi_options) {|c,code,method| blk.call(c) }
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# call-seq:
|
123
|
+
#
|
124
|
+
# Curl::Multi.http( [
|
125
|
+
# { :url => 'url1', :method => :post,
|
126
|
+
# :post_fields => {'field1' => 'value1', 'field2' => 'value2'} },
|
127
|
+
# { :url => 'url2', :method => :get,
|
128
|
+
# :follow_location => true, :max_redirects => 3 },
|
129
|
+
# { :url => 'url3', :method => :put, :put_data => File.open('file.txt','rb') },
|
130
|
+
# { :url => 'url4', :method => :head }
|
131
|
+
# ], {:pipeline => true})
|
132
|
+
#
|
133
|
+
# Blocking call to issue multiple HTTP requests with varying verb's.
|
134
|
+
#
|
135
|
+
# urls_with_config: is a hash of url's pointing to the easy handle options as well as the special option :method, that can by one of [:get, :post, :put, :delete, :head], when no verb is provided e.g. :method => nil -> GET is used
|
136
|
+
# multi_options: options for the multi handle
|
137
|
+
# blk: a callback, that yeilds when a handle is completed
|
138
|
+
#
|
139
|
+
def http(urls_with_config, multi_options={}, &blk)
|
140
|
+
m = Curl::Multi.new
|
141
|
+
# configure the multi handle
|
142
|
+
multi_options.each { |k,v| m.send("#{k}=", v) }
|
143
|
+
|
144
|
+
urls_with_config.each do|conf|
|
145
|
+
c = conf.dup # avoid being destructive to input
|
146
|
+
url = c.delete(:url)
|
147
|
+
method = c.delete(:method)
|
148
|
+
headers = c.delete(:headers)
|
149
|
+
|
150
|
+
easy = Curl::Easy.new(url)
|
151
|
+
|
152
|
+
case method
|
153
|
+
when :post
|
154
|
+
fields = c.delete(:post_fields)
|
155
|
+
# set the post post using the url fields
|
156
|
+
easy.post_body = fields.map{|f,k| "#{easy.escape(f)}=#{easy.escape(k)}"}.join('&')
|
157
|
+
when :put
|
158
|
+
easy.put_data = c.delete(:put_data)
|
159
|
+
when :head
|
160
|
+
easy.head = true
|
161
|
+
when :delete
|
162
|
+
easy.delete = true
|
163
|
+
when :get
|
164
|
+
else
|
165
|
+
# XXX: nil is treated like a GET
|
166
|
+
end
|
167
|
+
|
168
|
+
# headers is a special key
|
169
|
+
headers.each {|k,v| easy.headers[k] = v } if headers
|
170
|
+
|
171
|
+
#
|
172
|
+
# use the remaining options as specific configuration to the easy handle
|
173
|
+
# bad options should raise an undefined method error
|
174
|
+
#
|
175
|
+
c.each { |k,v| easy.send("#{k}=",v) }
|
176
|
+
|
177
|
+
easy.on_complete {|curl,code| blk.call(curl,code,method) } if blk
|
178
|
+
m.add(easy)
|
179
|
+
end
|
180
|
+
m.perform
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|