patron 0.13.3 → 0.13.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e766e45bfa71015943701e1340503df7f915d499cd001475649613175df4006d
4
- data.tar.gz: 8b3a7d7fcb6d555dfff1705685c8685b925c900dfec95200816580028ffa9d53
3
+ metadata.gz: f900014f4c841d87c4db54f3db3f33d9b624d5a6dde0eb9352b7a70cc6f330b7
4
+ data.tar.gz: 31b35805166302cb5527734b70c3607f9dbb951e2c1e3aafd465f7f8d5dbbb75
5
5
  SHA512:
6
- metadata.gz: 986e1bb343ea53368a6acb8f7d7ef84a48674408d4a08f7eabe1d2e2c6ff5c90f2afe0683d1e66ab751c72241a25c9f46c8e16c955c97b87c71a0bc2bd7db723
7
- data.tar.gz: c285b097c1fc4022fce3484184ac44d471340a496bc246142233e655bfdf0a28653484490ce5b1802fac4d5b396bea8ca03cfd1c803867f7c07ff3d8a789335c
6
+ metadata.gz: 1989abb0ba1be73ed08df186d05349c6519a90663a7263799a1e97e024e5183ff6de9f49ab329ebf4e3b49c4c571d0e3ca7efed3bc5f803ad8edbe96a6554ff6
7
+ data.tar.gz: dc03c98c0f363d7d7bf273b72a62954dadb590c1236baf06739f6fdebd6ae5007afc243dc526c97359165c19244868779fc4168cc9f972c32cbf8fef81a56521
@@ -0,0 +1,26 @@
1
+ name: Tests
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby-version: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2, 3.3, 3.4.1]
12
+
13
+ name: Specs - Ruby ${{ matrix.ruby-version }}
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - name: Install libcurl
17
+ run: |
18
+ sudo apt-get update
19
+ sudo apt-get -y install libcurl4 libcurl4-openssl-dev
20
+ - name: Set up Ruby ${{ matrix.ruby-version }}
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby-version }}
24
+ bundler-cache: true # 'bundle install' and cache
25
+ - name: Run tests
26
+ run: bundle exec rake
data/.travis.yml CHANGED
@@ -1,10 +1,13 @@
1
1
  rvm:
2
- - 1.9.3
3
- - 2.1.5
4
- - 2.2.2
5
- - 2.3.4
6
- - 2.4.1
7
- - 2.5.0
8
- sudo: false
2
+ - 2.3.8
3
+ - 2.4.5
4
+ - 2.5.3
5
+ - 2.6.1
6
+ - ruby-head
7
+
9
8
  dist: trusty
10
9
  cache: bundler
10
+
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: ruby-head
data/CHANGELOG.md CHANGED
@@ -1,6 +1,17 @@
1
+ ### 0.13.4
2
+
3
+ * Format README a bit better using code fences
4
+ * Depend explicitly on base64 for running tests
5
+ * Remove C code used for Ruby support below < 2.4 (GVL unlock etc.) and drop support for these versions
6
+ * In tests, close Tempfiles before allowing libcurl to use them (cookie jar)
7
+ * Fix incorrect ptr type in uses of `curl_easy_escape` / `_unescape`
8
+ * Define methods on `Session` as private instance methods from C instead of privatising them from Ruby
9
+ * Speed up the /slow endpoint test
10
+ * Remove use of `curl_easy_reset`. This should fix the bug with sessions resetting each other's state during `session_free`
11
+
1
12
  ### 0.13.3
2
13
 
3
- * Fix a number of specs that were failing due to use of threads
14
+ * Run Puma test servers in separate processes instead of threads
4
15
 
5
16
  ### 0.13.2
6
17
 
data/Gemfile CHANGED
@@ -1,4 +1,17 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in patron.gemspec
3
+ # Runtime dependencies are in the gemspec
4
4
  gemspec
5
+
6
+ # Gems below are only needed for Patron development
7
+ # and tests
8
+ gem "rake", ">= 12.3.3"
9
+ gem "rspec", ">= 2.3.0"
10
+ gem "simplecov", "~> 0.10"
11
+ gem "yard", "~> 0.9.20"
12
+ rack_version = RUBY_VERSION >= "3.3" ? "~> 2.2" : "~> 2.1.4"
13
+ gem "rack", rack_version
14
+ gem "puma", '~> 3.11'
15
+ gem "rake-compiler"
16
+ gem "webrick", "~> 1.8"
17
+ gem "base64"
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ # Patron
2
+
3
+ [![Build Status](https://travis-ci.org/toland/patron.svg?branch=master)](https://travis-ci.org/toland/patron)
4
+
1
5
  Patron is a Ruby HTTP client library based on libcurl. It does not try to expose
2
6
  the full "power" (read complexity) of libcurl but instead tries to provide a
3
7
  sane API while taking advantage of libcurl under the hood.
@@ -8,47 +12,63 @@ First, you instantiate a Session object. You can set a few
8
12
  default options on the Session instance that will be used by all subsequent
9
13
  requests:
10
14
 
11
- sess = Patron::Session.new
12
- sess.timeout = 10
13
- sess.base_url = "http://myserver.com:9900"
14
- sess.headers['User-Agent'] = 'myapp/1.0'
15
+ ```ruby
16
+ sess = Patron::Session.new
17
+ sess.timeout = 10
18
+ sess.base_url = "http://myserver.com:9900"
19
+ sess.headers['User-Agent'] = 'myapp/1.0'
20
+ ```
15
21
 
16
22
  You can set options with a hash in the constructor:
17
23
 
18
- sess = Patron::Session.new({ :timeout => 10,
19
- :base_url => 'http://myserver.com:9900',
20
- :headers => {'User-Agent' => 'myapp/1.0'} } )
24
+ ```ruby
25
+ sess = Patron::Session.new({ :timeout => 10,
26
+ :base_url => 'http://myserver.com:9900',
27
+ :headers => {'User-Agent' => 'myapp/1.0'} } )
28
+ ```
21
29
 
22
30
  Or the set options in a block:
23
31
 
24
- sess = Patron::Session.new do |patron|
25
- patron.timeout = 10
26
- patron.base_url = 'http://myserver.com:9900'
27
- patron.headers = {'User-Agent' => 'myapp/1.0'}
28
- end
32
+ ```ruby
33
+ sess = Patron::Session.new do |patron|
34
+ patron.timeout = 10
35
+ patron.base_url = 'http://myserver.com:9900'
36
+ patron.headers = {'User-Agent' => 'myapp/1.0'}
37
+ end
38
+ ```
29
39
 
30
40
  Output debug log:
31
41
 
32
- sess.enable_debug "/tmp/patron.debug"
42
+ ```ruby
43
+ sess.enable_debug "/tmp/patron.debug"
44
+ ```
33
45
 
34
46
  The Session is used to make HTTP requests.
35
47
 
36
- resp = sess.get("/foo/bar")
48
+ ```ruby
49
+ resp = sess.get("/foo/bar")
50
+ ```
37
51
 
38
52
  Requests return a Response object:
39
53
 
40
- if resp.status < 400
41
- puts resp.body
42
- end
54
+ ```ruby
55
+ if resp.status < 400
56
+ puts resp.body
57
+ end
58
+ ```
43
59
 
44
60
  The GET, HEAD, PUT, POST and DELETE operations are all supported.
45
61
 
46
- sess.put("/foo/baz", "some data")
47
- sess.delete("/foo/baz")
62
+ ```ruby
63
+ sess.put("/foo/baz", "some data")
64
+ sess.delete("/foo/baz")
65
+ ```
48
66
 
49
67
  You can ship custom headers with a single request:
50
68
 
51
- sess.post("/foo/stuff", "some data", {"Content-Type" => "text/plain"})
69
+ ```ruby
70
+ sess.post("/foo/stuff", "some data", {"Content-Type" => "text/plain"})
71
+ ```
52
72
 
53
73
  ## Threading
54
74
 
@@ -58,16 +78,18 @@ for doing concurrent requests. However, the actual code that interacts with libC
58
78
  so using multiple `Session` objects in different threads actually enables a high degree of parallelism.
59
79
  For sharing a resource of sessions between threads we recommend using the excellent [connection_pool](https://rubygems.org/gems/connection_pool) gem by Mike Perham.
60
80
 
61
- patron_pool = ConnectionPool.new(size: 5, timeout: 5) { Patron::Session.new }
62
- patron_pool.with do |session|
63
- session.get(...)
64
- end
81
+ ```ruby
82
+ patron_pool = ConnectionPool.new(size: 5, timeout: 5) { Patron::Session.new }
83
+ patron_pool.with do |session|
84
+ session.get(...)
85
+ end
86
+ ```
65
87
 
66
88
  Sharing Session objects between requests will also allow you to benefit from persistent connections (connection reuse), see below.
67
89
 
68
90
  ## Persistent connections
69
91
 
70
- Patron follows the libCURL guidelines on [connection reuse.](https://ec.haxx.se/libcurl-connectionreuse.html) If you create the Session
92
+ Patron follows the libCURL guidelines on [connection reuse.](https://everything.curl.dev/libcurl/connectionreuse.html) If you create the Session
71
93
  object once and use it for multiple requests, the same libCURL handle is going to be used across these requests and if requests go to
72
94
  the same hostname/port/protocol the connection should get reused.
73
95
 
@@ -81,8 +103,10 @@ that are currently active in the process.
81
103
 
82
104
  ## Requirements
83
105
 
84
- Patron uses encoding features of Ruby, so you need at least Ruby 1.9. Also, a
85
- recent version of libCURL is required. We recommend at least 7.19.4 because
106
+ Patron 1.0 and up requires MRI Ruby 2.3 or newer. The 0.x versions support
107
+ Ruby 1.9.3 and these versions get tagged and developed on the `v0.x` branch.
108
+
109
+ A recent version of libCURL is required. We recommend at least 7.19.4 because
86
110
  it [supports limiting the protocols](https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html),
87
111
  and that is very important for security - especially if you follow redirects.
88
112
 
@@ -90,9 +114,6 @@ On OSX the provided libcurl is sufficient if you are not using fork+SSL combinat
90
114
  You will have to install the libcurl development packages on Debian or Ubuntu. Other Linux systems are probably
91
115
  similar. For Windows we do not have an established build instruction at the moment, unfortunately.
92
116
 
93
- Versions of Patron below 1.0 will maintain compatibility down to Ruby 1.9.3,
94
- versions below 2.0 will maintain compatibility down to Ruby 2.3
95
-
96
117
  ## Forking webservers on macOS and SSL
97
118
 
98
119
  Currently, [an issue is at play](https://github.com/curl/curl/issues/788) with OSX builds of `curl` which use
@@ -19,9 +19,8 @@ if CONFIG['CC'] =~ /gcc/
19
19
  $CFLAGS << ' -pedantic -Wall'
20
20
  end
21
21
 
22
- $defs.push("-DUSE_TBR")
23
- $defs.push("-DHAVE_THREAD_H") if have_header('ruby/thread.h')
24
- $defs.push("-DHAVE_TBR") if have_func('rb_thread_blocking_region', 'ruby.h')
25
- $defs.push("-DHAVE_TCWOGVL") if have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
22
+ if CONFIG['CC'] =~ /clang/
23
+ $CFLAGS << ' -pedantic -Wall -Wno-void-pointer-to-enum-cast'
24
+ end
26
25
 
27
26
  create_makefile 'patron/session_ext'
@@ -1,7 +1,5 @@
1
1
  #include <ruby.h>
2
- #if defined(USE_TBR) && defined(HAVE_THREAD_H)
3
2
  #include <ruby/thread.h>
4
- #endif
5
3
  #include <sys/stat.h>
6
4
  #include <curl/curl.h>
7
5
  #include "membuffer.h"
@@ -29,6 +27,8 @@ static VALUE eAborted = Qnil;
29
27
 
30
28
  struct patron_curl_state {
31
29
  CURL* handle;
30
+ CURL* base_handle;
31
+ CURLSH* share;
32
32
  char* upload_buf;
33
33
  FILE* download_file;
34
34
  FILE* debug_file;
@@ -77,7 +77,7 @@ static size_t file_write_handler(void* stream, size_t size, size_t nmemb, FILE*
77
77
  static int call_user_rb_progress_blk(void* vd_curl_state) {
78
78
  struct patron_curl_state* state = (struct patron_curl_state*)vd_curl_state;
79
79
  // Invoke the block with the array
80
- VALUE blk_result = rb_funcall(state->user_progress_blk,
80
+ rb_funcall(state->user_progress_blk,
81
81
  rb_intern("call"), 4,
82
82
  LONG2NUM(state->dltotal),
83
83
  LONG2NUM(state->dlnow),
@@ -103,15 +103,7 @@ static int session_progress_handler(void* clientp, size_t dltotal, size_t dlnow,
103
103
  // `call_user_rb_progress_blk`. TODO: use the retval of that proc
104
104
  // to permit premature abort
105
105
  if(RTEST(state->user_progress_blk)) {
106
- // Even though it is not documented, rb_thread_call_with_gvl is available even when
107
- // rb_thread_call_without_gvl is not. See https://bugs.ruby-lang.org/issues/5543#note-4
108
- // > rb_thread_call_with_gvl() is globally-visible (but not in headers)
109
- // > for 1.9.3: https://bugs.ruby-lang.org/issues/4328
110
- #if (defined(HAVE_TBR) || defined(HAVE_TCWOGVL)) && defined(USE_TBR)
111
- rb_thread_call_with_gvl((void *(*)(void *)) call_user_rb_progress_blk, (void*)state);
112
- #else
113
- call_user_rb_progress_blk((void*)state);
114
- #endif
106
+ rb_thread_call_with_gvl((void *(*)(void *)) call_user_rb_progress_blk, (void*)state);
115
107
  }
116
108
 
117
109
  // Set the interrupt if the download byte limit has been reached
@@ -129,9 +121,10 @@ static int session_progress_handler(void* clientp, size_t dltotal, size_t dlnow,
129
121
  }
130
122
 
131
123
 
132
- /*----------------------------------------------------------------------------*/
133
- /* List of active curl sessions */
134
-
124
+ /*
125
+ List of active curl sessions, used exclusively to be able to set interrupts
126
+ for all of them if the Ruby interpreter gets shut down with libCURL requests still in flight.
127
+ */
135
128
  struct patron_curl_state_list {
136
129
  struct patron_curl_state *state;
137
130
  struct patron_curl_state_list *next;
@@ -184,20 +177,18 @@ static void session_close_debug_file(struct patron_curl_state *curl) {
184
177
  }
185
178
 
186
179
  /* Cleans up the patron_curl_state data when the Session object is garbage collected. */
187
- void session_free(struct patron_curl_state *curl) {
188
- if (curl->handle) {
189
- curl_easy_cleanup(curl->handle);
190
- curl->handle = NULL;
191
- }
180
+ void session_free(struct patron_curl_state *state) {
181
+ curl_easy_cleanup(state->base_handle);
182
+ curl_share_cleanup(state->share);
192
183
 
193
- session_close_debug_file(curl);
184
+ session_close_debug_file(state);
194
185
 
195
- membuffer_destroy( &curl->header_buffer );
196
- membuffer_destroy( &curl->body_buffer );
186
+ membuffer_destroy(&state->header_buffer);
187
+ membuffer_destroy(&state->body_buffer);
197
188
 
198
- cs_list_remove(curl);
189
+ cs_list_remove(state);
199
190
 
200
- free(curl);
191
+ free(state);
201
192
  }
202
193
 
203
194
  /* Allocates patron_curl_state data needed for a new Session object. */
@@ -215,16 +206,34 @@ VALUE session_alloc(VALUE klass) {
215
206
  reuse the TCP connection and can speed things up if the same resource - like a backend service -
216
207
  gets accessed over and over with requests.
217
208
  */
218
- state->handle = curl_easy_init();
219
- curl_easy_setopt(state->handle, CURLOPT_NOSIGNAL, 1);
220
- curl_easy_setopt(state->handle, CURLOPT_NOPROGRESS, 0);
221
- #if LIBCURL_VERSION_NUM >= 0x072000
222
- /* this is libCURLv7.32.0 or later, supports CURLOPT_XFERINFOFUNCTION */
223
- curl_easy_setopt(state->handle, CURLOPT_XFERINFOFUNCTION, &session_progress_handler);
224
- #else
225
- curl_easy_setopt(state->handle, CURLOPT_PROGRESSFUNCTION, &session_progress_handler);
226
- #endif
227
- curl_easy_setopt(state->handle, CURLOPT_PROGRESSDATA, state);
209
+ state->share = curl_share_init();
210
+ curl_share_setopt(state->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
211
+ curl_share_setopt(state->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
212
+ curl_share_setopt(state->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
213
+ curl_share_setopt(state->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
214
+ curl_share_setopt(state->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
215
+ state->base_handle = curl_easy_init();
216
+ curl_easy_setopt(state->base_handle, CURLOPT_SHARE, state->share);
217
+ curl_easy_setopt(state->base_handle, CURLOPT_WRITEFUNCTION, &session_write_handler);
218
+ curl_easy_setopt(state->base_handle, CURLOPT_WRITEDATA, &state->body_buffer);
219
+ curl_easy_setopt(state->base_handle, CURLOPT_HEADERFUNCTION, &session_write_handler);
220
+ curl_easy_setopt(state->base_handle, CURLOPT_HEADERDATA, &state->header_buffer);
221
+ curl_easy_setopt(state->base_handle, CURLOPT_NOSIGNAL, 1);
222
+ curl_easy_setopt(state->base_handle, CURLOPT_NOPROGRESS, 0);
223
+ #if LIBCURL_VERSION_NUM >= 0x072000
224
+ /* this is libCURLv7.32.0 or later, supports CURLOPT_XFERINFOFUNCTION */
225
+ curl_easy_setopt(state->base_handle, CURLOPT_XFERINFOFUNCTION, &session_progress_handler);
226
+ #else
227
+ curl_easy_setopt(state->base_handle, CURLOPT_PROGRESSFUNCTION, &session_progress_handler);
228
+ #endif
229
+ curl_easy_setopt(state->base_handle, CURLOPT_PROGRESSDATA, state);
230
+ #ifdef CURLPROTO_HTTP
231
+ // Security: do not allow Curl to go looking on gopher/SMTP etc.
232
+ // Must prevent situations like this:
233
+ // https://hackerone.com/reports/115748
234
+ curl_easy_setopt(state->base_handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
235
+ curl_easy_setopt(state->base_handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
236
+ #endif
228
237
 
229
238
  return obj;
230
239
  }
@@ -276,13 +285,22 @@ static VALUE session_escape(VALUE self, VALUE value) {
276
285
  char* escaped = NULL;
277
286
  VALUE retval = Qnil;
278
287
 
279
- struct patron_curl_state* state = curl_easy_init();
280
- escaped = curl_easy_escape(state->handle,
288
+ #if LIBCURL_VERSION_NUM >= 0x075200
289
+ /* this is libCURLv7.82.0 or later, the curl parameter is ignored */
290
+ CURL *curl = NULL;
291
+ #else
292
+ CURL* curl = curl_easy_init();
293
+ #endif
294
+ escaped = curl_easy_escape(curl,
281
295
  RSTRING_PTR(string),
282
296
  (int) RSTRING_LEN(string));
283
297
 
284
298
  retval = rb_str_new2(escaped);
285
- curl_easy_cleanup(state);
299
+ #if LIBCURL_VERSION_NUM >= 0x075200
300
+ /* this is libCURLv7.82.0 or later, the curl parameter is ignored */
301
+ #else
302
+ curl_easy_cleanup(curl);
303
+ #endif
286
304
  curl_free(escaped);
287
305
 
288
306
  return retval;
@@ -299,15 +317,24 @@ static VALUE session_unescape(VALUE self, VALUE value) {
299
317
  char* unescaped = NULL;
300
318
  VALUE retval = Qnil;
301
319
 
302
- struct patron_curl_state* state = curl_easy_init();
303
- unescaped = curl_easy_unescape(state->handle,
320
+ #if LIBCURL_VERSION_NUM >= 0x075200
321
+ /* this is libCURLv7.82.0 or later, the curl parameter is ignored */
322
+ CURL *curl = NULL;
323
+ #else
324
+ CURL *curl = curl_easy_init();
325
+ #endif
326
+ unescaped = curl_easy_unescape(curl,
304
327
  RSTRING_PTR(string),
305
328
  (int) RSTRING_LEN(string),
306
329
  NULL);
307
330
 
308
331
  retval = rb_str_new2(unescaped);
309
332
  curl_free(unescaped);
310
- curl_easy_cleanup(state);
333
+ #if LIBCURL_VERSION_NUM >= 0x075200
334
+ /* this is libCURLv7.82.0 or later, the curl parameter is ignored */
335
+ #else
336
+ curl_easy_cleanup(curl);
337
+ #endif
311
338
 
312
339
  return retval;
313
340
  }
@@ -433,7 +460,7 @@ static void set_request_body(struct patron_curl_state* state, VALUE stringable_o
433
460
  */
434
461
  static void set_options_from_request(VALUE self, VALUE request) {
435
462
  struct patron_curl_state* state = get_patron_curl_state(self);
436
- CURL* curl = state->handle;
463
+ CURL* curl = curl_easy_duphandle(state->base_handle);
437
464
 
438
465
  ID action = Qnil;
439
466
  VALUE headers = Qnil;
@@ -454,6 +481,9 @@ static void set_options_from_request(VALUE self, VALUE request) {
454
481
  VALUE download_byte_limit = rb_funcall(request, rb_intern("download_byte_limit"), 0);
455
482
  VALUE maybe_progress_proc = rb_funcall(request, rb_intern("progress_callback"), 0);
456
483
 
484
+ state->handle = curl;
485
+ curl_easy_setopt(curl, CURLOPT_SHARE, state->share);
486
+
457
487
  if (RTEST(download_byte_limit)) {
458
488
  state->download_byte_limit = FIX2INT(download_byte_limit);
459
489
  } else {
@@ -475,7 +505,7 @@ static void set_options_from_request(VALUE self, VALUE request) {
475
505
  }
476
506
 
477
507
  action = SYM2ID(action_name);
478
- if(rb_funcall(request, rb_intern("force_ipv4"), 0)) {
508
+ if (RTEST(rb_funcall(request, rb_intern("force_ipv4"), 0))) {
479
509
  curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
480
510
  }
481
511
  if (action == rb_intern("get")) {
@@ -578,13 +608,6 @@ static void set_options_from_request(VALUE self, VALUE request) {
578
608
  }
579
609
  curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
580
610
 
581
- #ifdef CURLPROTO_HTTP
582
- // Security: do not allow Curl to go looking on gopher/SMTP etc.
583
- // Must prevent situations like this:
584
- // https://hackerone.com/reports/115748
585
- curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
586
- curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
587
- #endif
588
611
 
589
612
  timeout = rb_funcall(request, rb_intern("timeout"), 0);
590
613
  if (RTEST(timeout)) {
@@ -722,11 +745,6 @@ static void set_options_from_request(VALUE self, VALUE request) {
722
745
  if (RTEST(buffer_size)) {
723
746
  curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, NUM2LONG(buffer_size));
724
747
  }
725
-
726
- if(state->debug_file) {
727
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
728
- curl_easy_setopt(curl, CURLOPT_STDERR, state->debug_file);
729
- }
730
748
  }
731
749
 
732
750
  /* Use the info in a Curl handle to create a new Response object. */
@@ -741,10 +759,10 @@ static VALUE create_response(VALUE self, CURL* curl, VALUE header_buffer, VALUE
741
759
  args[0] = rb_str_new2(effective_url);
742
760
 
743
761
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
744
- args[1] = INT2NUM(code);
762
+ args[1] = LONG2NUM(code);
745
763
 
746
764
  curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &count);
747
- args[2] = INT2NUM(count);
765
+ args[2] = LONG2NUM(count);
748
766
 
749
767
  args[3] = header_buffer;
750
768
  args[4] = body_buffer;
@@ -787,48 +805,19 @@ void session_ubf_abort(void* patron_state) {
787
805
  static VALUE perform_request(VALUE self) {
788
806
  struct patron_curl_state *state = get_patron_curl_state(self);
789
807
  CURL* curl = state->handle;
790
- membuffer* header_buffer = NULL;
791
- membuffer* body_buffer = NULL;
792
808
  CURLcode ret = 0;
793
809
 
794
810
  state->interrupt = 0; /* clear the interrupt flag */
795
811
 
796
- header_buffer = &state->header_buffer;
797
- body_buffer = &state->body_buffer;
798
-
799
- membuffer_clear(header_buffer);
800
- membuffer_clear(body_buffer);
801
-
802
- /* headers */
803
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &session_write_handler);
804
- curl_easy_setopt(curl, CURLOPT_HEADERDATA, header_buffer);
805
-
806
- /* body */
807
- if (!state->download_file) {
808
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &session_write_handler);
809
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, body_buffer);
810
- }
811
-
812
- #if (defined(HAVE_TBR) || defined(HAVE_TCWOGVL)) && defined(USE_TBR)
813
- #if defined(HAVE_TCWOGVL)
814
- ret = (CURLcode) rb_thread_call_without_gvl(
815
- (void *(*)(void *)) curl_easy_perform, curl,
816
- session_ubf_abort, (void*)state
817
- );
818
- #else
819
- ret = (CURLcode) rb_thread_blocking_region(
820
- (rb_blocking_function_t*) curl_easy_perform, curl,
821
- session_ubf_abort, (void*)state
822
- );
823
- #endif
824
- #else
825
- ret = curl_easy_perform(curl);
826
- #endif
812
+ ret = (CURLcode) rb_thread_call_without_gvl(
813
+ (void *(*)(void *)) curl_easy_perform, curl,
814
+ session_ubf_abort, (void*)state
815
+ );
827
816
 
828
817
  if (CURLE_OK == ret) {
829
- VALUE header_str = membuffer_to_rb_str(header_buffer);
818
+ VALUE header_str = membuffer_to_rb_str(&state->header_buffer);
830
819
  VALUE body_str = Qnil;
831
- if (!state->download_file) { body_str = membuffer_to_rb_str(body_buffer); }
820
+ if (!state->download_file) { body_str = membuffer_to_rb_str(&state->body_buffer); }
832
821
 
833
822
  curl_easy_setopt(curl, CURLOPT_COOKIELIST, "FLUSH"); // Flush cookies to the cookie jar
834
823
 
@@ -843,16 +832,19 @@ static VALUE perform_request(VALUE self) {
843
832
  */
844
833
  static VALUE cleanup(VALUE self) {
845
834
  struct patron_curl_state *state = get_patron_curl_state(self);
846
- curl_easy_reset(state->handle);
835
+ curl_easy_cleanup(state->handle);
847
836
 
848
837
  if (state->headers) {
849
838
  curl_slist_free_all(state->headers);
850
839
  state->headers = NULL;
851
840
  }
841
+ membuffer_clear(&state->header_buffer);
852
842
 
853
843
  if (state->download_file) {
854
844
  fclose(state->download_file);
855
845
  state->download_file = NULL;
846
+ } else {
847
+ membuffer_clear(&state->body_buffer);
856
848
  }
857
849
 
858
850
  if (state->request_body_file) {
@@ -912,7 +904,7 @@ static VALUE session_interrupt(VALUE self) {
912
904
  */
913
905
  static VALUE add_cookie_file(VALUE self, VALUE file) {
914
906
  struct patron_curl_state *state = get_patron_curl_state(self);
915
- CURL* curl = state->handle;
907
+ CURL* curl = state->base_handle;
916
908
  char* file_path = NULL;
917
909
 
918
910
  // FIXME: http://websystemsengineering.blogspot.nl/2013/03/curloptcookiefile-vs-curloptcookiejar.html
@@ -933,7 +925,8 @@ static VALUE add_cookie_file(VALUE self, VALUE file) {
933
925
  */
934
926
  static VALUE set_debug_file(VALUE self, VALUE file) {
935
927
  struct patron_curl_state *state = get_patron_curl_state(self);
936
- char* file_path = RSTRING_PTR(file);
928
+ CURL *curl = state->base_handle;
929
+ char *file_path = RSTRING_PTR(file);
937
930
 
938
931
  session_close_debug_file(state);
939
932
 
@@ -942,6 +935,8 @@ static VALUE set_debug_file(VALUE self, VALUE file) {
942
935
  } else {
943
936
  state->debug_file = stderr;
944
937
  }
938
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
939
+ curl_easy_setopt(curl, CURLOPT_STDERR, state->debug_file);
945
940
 
946
941
  return self;
947
942
  }
@@ -986,11 +981,11 @@ void Init_session_ext() {
986
981
  rb_define_singleton_method(cSession, "unescape", session_unescape, 1);
987
982
  rb_define_method(cSession, "unescape", session_unescape, 1);
988
983
 
989
- rb_define_method(cSession, "handle_request", session_handle_request, 1);
984
+ rb_define_private_method(cSession, "handle_request", session_handle_request, 1);
990
985
  rb_define_method(cSession, "reset", session_interrupt, 0);
991
986
  rb_define_method(cSession, "interrupt", session_interrupt, 0);
992
- rb_define_method(cSession, "add_cookie_file", add_cookie_file, 1);
993
- rb_define_method(cSession, "set_debug_file", set_debug_file, 1);
987
+ rb_define_private_method(cSession, "add_cookie_file", add_cookie_file, 1);
988
+ rb_define_private_method(cSession, "set_debug_file", set_debug_file, 1);
994
989
  rb_define_alias(cSession, "urlencode", "escape");
995
990
  rb_define_alias(cSession, "urldecode", "unescape");
996
991
 
@@ -34,8 +34,8 @@ module Patron
34
34
  :low_speed_time, :low_speed_limit, :progress_callback
35
35
  ]
36
36
 
37
- attr_reader *READER_VARS
38
- attr_writer *WRITER_VARS
37
+ attr_reader(*READER_VARS)
38
+ attr_writer(*WRITER_VARS)
39
39
 
40
40
  # Set the type of authentication to use for this request.
41
41
  #
@@ -103,8 +103,6 @@ module Patron
103
103
  # @see low_speed_time
104
104
  attr_accessor :low_speed_limit
105
105
 
106
- private :handle_request, :add_cookie_file, :set_debug_file
107
-
108
106
  # @return [#call, nil] callable object that will be called with 4 arguments
109
107
  # during request/response execution - `dltotal`, `dlnow`, `ultotal`, `ulnow`.
110
108
  # All these arguments are in bytes.
@@ -146,9 +144,9 @@ module Patron
146
144
  if file_path
147
145
  path = Pathname(file_path).expand_path
148
146
 
149
- if !File.exists?(file_path) && !File.writable?(path.dirname)
147
+ if !File.exist?(file_path) && !File.writable?(path.dirname)
150
148
  raise ArgumentError, "Can't create file #{path} (permission error)"
151
- elsif File.exists?(file_path) && !File.writable?(file_path)
149
+ elsif File.exist?(file_path) && !File.writable?(file_path)
152
150
  raise ArgumentError, "Can't read or write file #{path} (permission error)"
153
151
  end
154
152
  else
@@ -1,3 +1,3 @@
1
1
  module Patron
2
- VERSION = "0.13.3"
2
+ VERSION = "0.13.4"
3
3
  end