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 CHANGED
@@ -17,6 +17,7 @@ requests:
17
17
  sess.timeout = 10
18
18
  sess.base_url = "http://myserver.com:9900"
19
19
  sess.headers['User-Agent'] = 'myapp/1.0'
20
+ sess.enable_debug "/tmp/patron.debug"
20
21
 
21
22
  The Session is used to make HTTP requests.
22
23
 
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 4
4
- :patch: 9
4
+ :patch: 10
5
5
  :build:
@@ -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'
@@ -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
+
@@ -0,0 +1,10 @@
1
+ module Patron
2
+ module ProxyType
3
+ HTTP
4
+ HTTP_1_0
5
+ SOCKS4
6
+ SOCKS5
7
+ SOCKS4A
8
+ SOCKS5_HOSTNAME
9
+ end
10
+ end
@@ -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
@@ -50,9 +50,12 @@ module Patron
50
50
  # Username and password for http authentication
51
51
  attr_accessor :username, :password
52
52
 
53
- # HTTP proxy URL
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
- private :ext_initialize, :handle_request, :enable_cookie_session
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
- # Makes this session handle cookies and store them in in +file+.
79
- # If file is nil they will be stored in memory. Otherwise the +file+
80
- # must be readable and writable. Calling multiple times will add more files.
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, "Cant read or write file #{path} (permission error)"
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
@@ -21,7 +21,7 @@
21
21
  ## THE SOFTWARE.
22
22
  ##
23
23
  ## -------------------------------------------------------------------
24
- require File.dirname(__FILE__) + '/spec_helper.rb'
24
+ require File.expand_path("./spec") + '/spec_helper.rb'
25
25
 
26
26
  describe Patron do
27
27
 
data/spec/request_spec.rb CHANGED
@@ -21,7 +21,7 @@
21
21
  ## THE SOFTWARE.
22
22
  ##
23
23
  ## -------------------------------------------------------------------
24
- require File.dirname(__FILE__) + '/spec_helper.rb'
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
@@ -21,7 +21,7 @@
21
21
  ## THE SOFTWARE.
22
22
  ##
23
23
  ## -------------------------------------------------------------------
24
- require File.dirname(__FILE__) + '/spec_helper.rb'
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.dirname(__FILE__) + '/spec_helper.rb'
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: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 4
9
- - 9
10
- version: 0.4.9
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-09-14 00:00:00 -05:00
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