patron 0.4.9 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|