patron 0.4.9 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +1 -0
- data/VERSION.yml +1 -1
- data/ext/patron/extconf.rb +1 -1
- data/ext/patron/session_ext.c +98 -4
- data/lib/patron/proxy_type.rb +10 -0
- data/lib/patron/request.rb +11 -3
- data/lib/patron/session.rb +26 -6
- data/spec/patron_spec.rb +1 -1
- data/spec/request_spec.rb +12 -1
- data/spec/response_spec.rb +1 -1
- data/spec/session_spec.rb +25 -1
- metadata +5 -4
data/README.txt
CHANGED
data/VERSION.yml
CHANGED
data/ext/patron/extconf.rb
CHANGED
@@ -23,7 +23,7 @@
|
|
23
23
|
## -------------------------------------------------------------------
|
24
24
|
|
25
25
|
if RUBY_PLATFORM =~ /darwin10\.0/
|
26
|
-
ENV['ARCHFLAGS'] = '-arch x86_64'
|
26
|
+
ENV['ARCHFLAGS'] = '-force_cpusubtype_ALL -mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64'
|
27
27
|
end
|
28
28
|
|
29
29
|
require 'mkmf'
|
data/ext/patron/session_ext.c
CHANGED
@@ -26,6 +26,7 @@
|
|
26
26
|
#include <curl/curl.h>
|
27
27
|
|
28
28
|
static VALUE mPatron = Qnil;
|
29
|
+
static VALUE mProxyType = Qnil;
|
29
30
|
static VALUE cSession = Qnil;
|
30
31
|
static VALUE cRequest = Qnil;
|
31
32
|
static VALUE ePatronError = Qnil;
|
@@ -43,11 +44,13 @@ struct curl_state {
|
|
43
44
|
char* upload_buf;
|
44
45
|
FILE* download_file;
|
45
46
|
FILE* upload_file;
|
47
|
+
FILE* debug_file;
|
46
48
|
char error_buf[CURL_ERROR_SIZE];
|
47
49
|
struct curl_slist* headers;
|
50
|
+
struct curl_httppost* post;
|
51
|
+
struct curl_httppost* last;
|
48
52
|
};
|
49
53
|
|
50
|
-
|
51
54
|
//------------------------------------------------------------------------------
|
52
55
|
// Curl Callbacks
|
53
56
|
//
|
@@ -78,6 +81,12 @@ static size_t session_read_handler(char* stream, size_t size, size_t nmemb, char
|
|
78
81
|
// Cleans up the Curl handle when the Session object is garbage collected.
|
79
82
|
void session_free(struct curl_state *curl) {
|
80
83
|
curl_easy_cleanup(curl->handle);
|
84
|
+
|
85
|
+
if (curl->debug_file) {
|
86
|
+
fclose(curl->debug_file);
|
87
|
+
curl->debug_file = NULL;
|
88
|
+
}
|
89
|
+
|
81
90
|
free(curl);
|
82
91
|
}
|
83
92
|
|
@@ -106,6 +115,8 @@ VALUE session_ext_initialize(VALUE self) {
|
|
106
115
|
Data_Get_Struct(self, struct curl_state, state);
|
107
116
|
|
108
117
|
state->handle = curl_easy_init();
|
118
|
+
state->post = NULL;
|
119
|
+
state->last = NULL;
|
109
120
|
|
110
121
|
return self;
|
111
122
|
}
|
@@ -159,6 +170,31 @@ static int each_http_header(VALUE header_key, VALUE header_value, VALUE self) {
|
|
159
170
|
return 0;
|
160
171
|
}
|
161
172
|
|
173
|
+
static int formadd_values(VALUE data_key, VALUE data_value, VALUE self) {
|
174
|
+
struct curl_state *state;
|
175
|
+
Data_Get_Struct(self, struct curl_state, state);
|
176
|
+
|
177
|
+
VALUE name = rb_obj_as_string(data_key);
|
178
|
+
VALUE value = rb_obj_as_string(data_value);
|
179
|
+
|
180
|
+
curl_formadd(&state->post, &state->last, CURLFORM_PTRNAME, RSTRING_PTR(name),
|
181
|
+
CURLFORM_PTRCONTENTS, RSTRING_PTR(value), CURLFORM_END);
|
182
|
+
return 0;
|
183
|
+
}
|
184
|
+
|
185
|
+
static int formadd_files(VALUE data_key, VALUE data_value, VALUE self) {
|
186
|
+
struct curl_state *state;
|
187
|
+
Data_Get_Struct(self, struct curl_state, state);
|
188
|
+
|
189
|
+
VALUE name = rb_obj_as_string(data_key);
|
190
|
+
VALUE value = rb_obj_as_string(data_value);
|
191
|
+
|
192
|
+
curl_formadd(&state->post, &state->last, CURLFORM_PTRNAME, RSTRING_PTR(name),
|
193
|
+
CURLFORM_FILE, RSTRING_PTR(value), CURLFORM_END);
|
194
|
+
|
195
|
+
return 0;
|
196
|
+
}
|
197
|
+
|
162
198
|
static void set_chunked_encoding(struct curl_state *state) {
|
163
199
|
state->headers = curl_slist_append(state->headers, "Transfer-Encoding: chunked");
|
164
200
|
}
|
@@ -189,7 +225,6 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
189
225
|
|
190
226
|
rb_hash_foreach(headers, each_http_header, self);
|
191
227
|
}
|
192
|
-
|
193
228
|
ID action = SYM2ID(rb_iv_get(request, "@action"));
|
194
229
|
if (action == rb_intern("get")) {
|
195
230
|
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
|
@@ -204,8 +239,9 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
204
239
|
} else if (action == rb_intern("post") || action == rb_intern("put")) {
|
205
240
|
VALUE data = rb_iv_get(request, "@upload_data");
|
206
241
|
VALUE filename = rb_iv_get(request, "@file_name");
|
242
|
+
VALUE multipart = rb_iv_get(request, "@multipart");
|
207
243
|
|
208
|
-
if (!NIL_P(data)) {
|
244
|
+
if (!NIL_P(data) && NIL_P(multipart)) {
|
209
245
|
state->upload_buf = StringValuePtr(data);
|
210
246
|
int len = RSTRING_LEN(data);
|
211
247
|
|
@@ -219,7 +255,7 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
219
255
|
curl_easy_setopt(curl, CURLOPT_READDATA, &state->upload_buf);
|
220
256
|
curl_easy_setopt(curl, CURLOPT_INFILESIZE, len);
|
221
257
|
}
|
222
|
-
} else if (!NIL_P(filename)) {
|
258
|
+
} else if (!NIL_P(filename) && NIL_P(multipart)) {
|
223
259
|
set_chunked_encoding(state);
|
224
260
|
|
225
261
|
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
|
@@ -230,6 +266,20 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
230
266
|
|
231
267
|
state->upload_file = open_file(filename, "r");
|
232
268
|
curl_easy_setopt(curl, CURLOPT_READDATA, state->upload_file);
|
269
|
+
} else if (!NIL_P(multipart)) {
|
270
|
+
if (action == rb_intern("post")) {
|
271
|
+
if(!NIL_P(data) && !NIL_P(filename)) {
|
272
|
+
if (rb_type(data) == T_HASH && rb_type(filename) == T_HASH) {
|
273
|
+
rb_hash_foreach(data, formadd_values, self);
|
274
|
+
rb_hash_foreach(filename, formadd_files, self);
|
275
|
+
} else { rb_raise(rb_eArgError, "Data and Filename must be passed in a hash.");}
|
276
|
+
}
|
277
|
+
curl_easy_setopt(curl, CURLOPT_HTTPPOST, state->post);
|
278
|
+
|
279
|
+
} else {
|
280
|
+
rb_raise(rb_eArgError, "Multipart PUT not supported");
|
281
|
+
}
|
282
|
+
|
233
283
|
} else {
|
234
284
|
rb_raise(rb_eArgError, "Must provide either data or a filename when doing a PUT or POST");
|
235
285
|
}
|
@@ -271,6 +321,11 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
271
321
|
curl_easy_setopt(curl, CURLOPT_PROXY, StringValuePtr(proxy));
|
272
322
|
}
|
273
323
|
|
324
|
+
VALUE proxy_type = rb_iv_get(request, "@proxy_type");
|
325
|
+
if (!NIL_P(proxy_type)) {
|
326
|
+
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, FIX2INT(proxy_type));
|
327
|
+
}
|
328
|
+
|
274
329
|
VALUE credentials = rb_funcall(request, rb_intern("credentials"), 0);
|
275
330
|
if (!NIL_P(credentials)) {
|
276
331
|
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, FIX2INT(rb_iv_get(request, "@auth_type")));
|
@@ -282,6 +337,16 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
282
337
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
283
338
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
|
284
339
|
}
|
340
|
+
|
341
|
+
VALUE buffer_size = rb_iv_get(request, "@buffer_size");
|
342
|
+
if (!NIL_P(buffer_size)) {
|
343
|
+
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, FIX2INT(buffer_size));
|
344
|
+
}
|
345
|
+
|
346
|
+
if(state->debug_file) {
|
347
|
+
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
348
|
+
curl_easy_setopt(curl, CURLOPT_STDERR, state->debug_file);
|
349
|
+
}
|
285
350
|
}
|
286
351
|
|
287
352
|
// Use the info in a Curl handle to create a new Response object.
|
@@ -405,6 +470,25 @@ VALUE enable_cookie_session(VALUE self, VALUE file) {
|
|
405
470
|
return Qnil;
|
406
471
|
}
|
407
472
|
|
473
|
+
VALUE set_debug_file(VALUE self, VALUE file) {
|
474
|
+
struct curl_state *state;
|
475
|
+
Data_Get_Struct(self, struct curl_state, state);
|
476
|
+
char* file_path = RSTRING_PTR(file);
|
477
|
+
|
478
|
+
if(state->debug_file){
|
479
|
+
fclose(state->debug_file);
|
480
|
+
state->debug_file = NULL;
|
481
|
+
}
|
482
|
+
|
483
|
+
if(file_path != NULL && strlen(file_path) != 0) {
|
484
|
+
state->debug_file = open_file(file, "w");
|
485
|
+
} else {
|
486
|
+
state->debug_file = stderr;
|
487
|
+
}
|
488
|
+
|
489
|
+
return Qnil;
|
490
|
+
}
|
491
|
+
|
408
492
|
//------------------------------------------------------------------------------
|
409
493
|
// Extension initialization
|
410
494
|
//
|
@@ -436,8 +520,18 @@ void Init_session_ext() {
|
|
436
520
|
rb_define_method(cSession, "unescape", session_unescape, 1);
|
437
521
|
rb_define_method(cSession, "handle_request", session_handle_request, 1);
|
438
522
|
rb_define_method(cSession, "enable_cookie_session", enable_cookie_session, 1);
|
523
|
+
rb_define_method(cSession, "set_debug_file", set_debug_file, 1);
|
439
524
|
|
440
525
|
rb_define_const(cRequest, "AuthBasic", INT2FIX(CURLAUTH_BASIC));
|
441
526
|
rb_define_const(cRequest, "AuthDigest", INT2FIX(CURLAUTH_DIGEST));
|
442
527
|
rb_define_const(cRequest, "AuthAny", INT2FIX(CURLAUTH_ANY));
|
528
|
+
|
529
|
+
mProxyType = rb_define_module_under(mPatron, "ProxyType");
|
530
|
+
rb_define_const(mProxyType, "HTTP", INT2FIX(CURLPROXY_HTTP));
|
531
|
+
rb_define_const(mProxyType, "HTTP_1_0", INT2FIX(CURLPROXY_HTTP_1_0));
|
532
|
+
rb_define_const(mProxyType, "SOCKS4", INT2FIX(CURLPROXY_SOCKS4));
|
533
|
+
rb_define_const(mProxyType, "SOCKS5", INT2FIX(CURLPROXY_SOCKS5));
|
534
|
+
rb_define_const(mProxyType, "SOCKS4A", INT2FIX(CURLPROXY_SOCKS4A));
|
535
|
+
rb_define_const(mProxyType, "SOCKS5_HOSTNAME", INT2FIX(CURLPROXY_SOCKS5_HOSTNAME));
|
443
536
|
}
|
537
|
+
|
data/lib/patron/request.rb
CHANGED
@@ -40,8 +40,8 @@ module Patron
|
|
40
40
|
@max_redirects = -1
|
41
41
|
end
|
42
42
|
|
43
|
-
attr_accessor :url, :username, :password, :file_name, :proxy, :auth_type, :insecure
|
44
|
-
attr_reader :action, :timeout, :connect_timeout, :max_redirects, :headers
|
43
|
+
attr_accessor :url, :username, :password, :file_name, :proxy, :proxy_type, :auth_type, :insecure, :multipart
|
44
|
+
attr_reader :action, :timeout, :connect_timeout, :max_redirects, :headers, :buffer_size
|
45
45
|
attr_reader :auth_type
|
46
46
|
|
47
47
|
# Set the type of authentication to use for this request.
|
@@ -69,7 +69,7 @@ module Patron
|
|
69
69
|
def upload_data=(data)
|
70
70
|
@upload_data = case data
|
71
71
|
when Hash
|
72
|
-
hash_to_string(data)
|
72
|
+
self.multipart ? data : hash_to_string(data)
|
73
73
|
else
|
74
74
|
data
|
75
75
|
end
|
@@ -119,6 +119,14 @@ module Patron
|
|
119
119
|
@headers = new_headers
|
120
120
|
end
|
121
121
|
|
122
|
+
def buffer_size=(buffer_size)
|
123
|
+
if buffer_size != nil && buffer_size.to_i < 1
|
124
|
+
raise ArgumentError, "Buffer size must be a positive integer greater than 0 or nil"
|
125
|
+
end
|
126
|
+
|
127
|
+
@buffer_size = buffer_size != nil ? buffer_size.to_i : nil
|
128
|
+
end
|
129
|
+
|
122
130
|
def action_name
|
123
131
|
@action.to_s.upcase
|
124
132
|
end
|
data/lib/patron/session.rb
CHANGED
@@ -50,9 +50,12 @@ module Patron
|
|
50
50
|
# Username and password for http authentication
|
51
51
|
attr_accessor :username, :password
|
52
52
|
|
53
|
-
#
|
53
|
+
# Proxy URL in cURL format ('hostname:8080')
|
54
54
|
attr_accessor :proxy
|
55
55
|
|
56
|
+
# Proxy type (default is HTTP), see constants under ProxyType for supported types.
|
57
|
+
attr_accessor :proxy_type
|
58
|
+
|
56
59
|
# Standard set of headers that are used in all requests.
|
57
60
|
attr_reader :headers
|
58
61
|
|
@@ -63,7 +66,11 @@ module Patron
|
|
63
66
|
# Does this session stricly verify SSL certificates?
|
64
67
|
attr_accessor :insecure
|
65
68
|
|
66
|
-
|
69
|
+
# Set the buffer size for this request. This option will
|
70
|
+
# only be set if buffer_size is non-nil
|
71
|
+
attr_accessor :buffer_size
|
72
|
+
|
73
|
+
private :ext_initialize, :handle_request, :enable_cookie_session, :set_debug_file
|
67
74
|
|
68
75
|
# Create a new Session object.
|
69
76
|
def initialize
|
@@ -75,9 +82,9 @@ module Patron
|
|
75
82
|
@auth_type = :basic
|
76
83
|
end
|
77
84
|
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
85
|
+
# Turn on cookie handling for this session, storing them in memory by
|
86
|
+
# default or in +file+ if specified. The +file+ must be readable and
|
87
|
+
# writable. Calling multiple times will add more files.
|
81
88
|
def handle_cookies(file = nil)
|
82
89
|
if file
|
83
90
|
path = Pathname(file).expand_path
|
@@ -85,13 +92,18 @@ module Patron
|
|
85
92
|
raise ArgumentError, "Can't create file #{path} (permission error)"
|
86
93
|
end
|
87
94
|
unless File.readable?(file) or File.writable?(path)
|
88
|
-
raise ArgumentError, "
|
95
|
+
raise ArgumentError, "Can't read or write file #{path} (permission error)"
|
89
96
|
end
|
90
97
|
end
|
91
98
|
enable_cookie_session(path.to_s)
|
92
99
|
self
|
93
100
|
end
|
94
101
|
|
102
|
+
# Enable debug output to stderr or to specified +file+.
|
103
|
+
def enable_debug(file = nil)
|
104
|
+
set_debug_file(file.to_s)
|
105
|
+
end
|
106
|
+
|
95
107
|
###################################################################
|
96
108
|
### Standard HTTP methods
|
97
109
|
###
|
@@ -143,6 +155,11 @@ module Patron
|
|
143
155
|
request(:post, url, headers, :file => filename)
|
144
156
|
end
|
145
157
|
|
158
|
+
# Uploads the contents of a file and data to the specified +url+ using HTTP POST.
|
159
|
+
def post_multipart(url, data, filename, headers = {})
|
160
|
+
request(:post, url, headers, {:data => data, :file => filename, :multipart => true})
|
161
|
+
end
|
162
|
+
|
146
163
|
###################################################################
|
147
164
|
### WebDAV methods
|
148
165
|
###
|
@@ -170,11 +187,14 @@ module Patron
|
|
170
187
|
req.headers = self.headers.merge(headers)
|
171
188
|
req.username = self.username
|
172
189
|
req.password = self.password
|
190
|
+
req.multipart = options[:multipart]
|
173
191
|
req.upload_data = options[:data]
|
174
192
|
req.file_name = options[:file]
|
175
193
|
req.proxy = proxy
|
194
|
+
req.proxy_type = proxy_type
|
176
195
|
req.auth_type = auth_type
|
177
196
|
req.insecure = insecure
|
197
|
+
req.buffer_size = buffer_size
|
178
198
|
|
179
199
|
req.url = self.base_url.to_s + url.to_s
|
180
200
|
raise ArgumentError, "Empty URL" if req.url.empty?
|
data/spec/patron_spec.rb
CHANGED
data/spec/request_spec.rb
CHANGED
@@ -21,7 +21,7 @@
|
|
21
21
|
## THE SOFTWARE.
|
22
22
|
##
|
23
23
|
## -------------------------------------------------------------------
|
24
|
-
require File.
|
24
|
+
require File.expand_path("./spec") + '/spec_helper.rb'
|
25
25
|
|
26
26
|
|
27
27
|
describe Patron::Request do
|
@@ -72,4 +72,15 @@ describe Patron::Request do
|
|
72
72
|
|
73
73
|
end
|
74
74
|
|
75
|
+
describe :buffer_size do
|
76
|
+
|
77
|
+
it "should raise an exception when assigned a negative number" do
|
78
|
+
lambda {@request.buffer_size = -1}.should raise_error(ArgumentError)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should raise an exception when assigned 0" do
|
82
|
+
lambda {@request.buffer_size = 0}.should raise_error(ArgumentError)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
75
86
|
end
|
data/spec/response_spec.rb
CHANGED
@@ -21,7 +21,7 @@
|
|
21
21
|
## THE SOFTWARE.
|
22
22
|
##
|
23
23
|
## -------------------------------------------------------------------
|
24
|
-
require File.
|
24
|
+
require File.expand_path("./spec") + '/spec_helper.rb'
|
25
25
|
require 'webrick'
|
26
26
|
require 'base64'
|
27
27
|
require 'fileutils'
|
data/spec/session_spec.rb
CHANGED
@@ -21,7 +21,7 @@
|
|
21
21
|
## THE SOFTWARE.
|
22
22
|
##
|
23
23
|
## -------------------------------------------------------------------
|
24
|
-
require File.
|
24
|
+
require File.expand_path("./spec") + '/spec_helper.rb'
|
25
25
|
require 'webrick'
|
26
26
|
require 'base64'
|
27
27
|
require 'fileutils'
|
@@ -174,6 +174,12 @@ describe Patron::Session do
|
|
174
174
|
body = YAML::load(response.body)
|
175
175
|
body.request_method.should == "POST"
|
176
176
|
end
|
177
|
+
|
178
|
+
it "should upload a multipart with :post" do
|
179
|
+
response = @session.post_multipart("/test", { :test_data => "123" }, { :test_file => "VERSION.yml" } )
|
180
|
+
body = YAML::load(response.body)
|
181
|
+
body.request_method.should == "POST"
|
182
|
+
end
|
177
183
|
|
178
184
|
it "should raise when no file is provided to :post" do
|
179
185
|
lambda { @session.post_file("/test", nil) }.should raise_error(ArgumentError)
|
@@ -220,6 +226,24 @@ describe Patron::Session do
|
|
220
226
|
threads.each {|t| t.join }
|
221
227
|
end
|
222
228
|
|
229
|
+
it "should limit the buffer_size" do
|
230
|
+
# Buffer size is tricky to test, as it only affects the buffer size for each
|
231
|
+
# read and it's not really visible at this, higher level. It's also only a
|
232
|
+
# suggestion rather than a command so it may not even take affect. Currently
|
233
|
+
# we just test that the response completes without any issues, it would be nice
|
234
|
+
# to have a more robust test here.
|
235
|
+
@session.buffer_size = 1
|
236
|
+
|
237
|
+
body = nil
|
238
|
+
|
239
|
+
lambda {
|
240
|
+
response = @session.get("/test")
|
241
|
+
body = YAML::load(response.body)
|
242
|
+
}.should_not raise_error
|
243
|
+
|
244
|
+
body.request_method.should == "GET"
|
245
|
+
end
|
246
|
+
|
223
247
|
def encode_authz(user, passwd)
|
224
248
|
"Basic " + Base64.encode64("#{user}:#{passwd}").strip
|
225
249
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: patron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
9
|
+
- 10
|
10
|
+
version: 0.4.10
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Phillip Toland
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-06 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- ext/patron/session_ext.c
|
38
38
|
- lib/patron.rb
|
39
39
|
- lib/patron/error.rb
|
40
|
+
- lib/patron/proxy_type.rb
|
40
41
|
- lib/patron/request.rb
|
41
42
|
- lib/patron/response.rb
|
42
43
|
- lib/patron/session.rb
|