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 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