patron 0.13.3 → 0.14.0
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 +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +79 -0
- data/.travis.yml +10 -7
- data/CHANGELOG.md +32 -1
- data/Gemfile +15 -1
- data/README.md +80 -30
- data/ext/patron/extconf.rb +7 -4
- data/ext/patron/session_ext.c +360 -155
- data/lib/patron/error.rb +4 -0
- data/lib/patron/request.rb +8 -6
- data/lib/patron/response.rb +5 -0
- data/lib/patron/session.rb +19 -4
- data/lib/patron/version.rb +1 -1
- data/patron.gemspec +8 -15
- data/script/generate_client_certs +65 -0
- data/spec/request_spec.rb +12 -0
- data/spec/session_spec.rb +287 -67
- data/spec/session_ssl_spec.rb +163 -27
- data/spec/spec_helper.rb +16 -7
- data/spec/support/certs/client.p12 +0 -0
- data/spec/support/certs/client_ca.pem +20 -0
- data/spec/support/certs/client_cert.pem +20 -0
- data/spec/support/certs/client_key.pem +30 -0
- data/spec/support/certs/rogue_client.p12 +0 -0
- data/spec/support/config.ru +18 -3
- data/spec/support/fork.rb +70 -0
- data/spec/support/raw_request_capture.rb +93 -0
- data/spec/support/test_server.rb +6 -3
- data/spec/support/valgrind.supp +2 -0
- metadata +22 -125
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 355b38921e6368ee1ad7815cd78ad9135630b10c82d60365978747e4189be390
|
|
4
|
+
data.tar.gz: efbebf2f2e6393e93411d5583c9062b3170e0bf4e30ae370d1e998110b19c3c3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 760418bb81862c742f9873caa2513a6ed60093a084145b77e9f21996f041b0e848a0a6914e0248c0dca806bc5313c59eed68afd2546c828218a2e0aa52734ecf
|
|
7
|
+
data.tar.gz: 77510952d581ea162ef55e93260fc8188e58b5f919dd97912c6d7b40863c94e2c517027c6de5fe2b5041431fb9645c85ad674b08f37ce7e2fcdb37b4f16f2da2
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-24.04
|
|
8
|
+
continue-on-error: ${{ matrix.allow-failure || false }}
|
|
9
|
+
strategy:
|
|
10
|
+
fail-fast: false
|
|
11
|
+
matrix:
|
|
12
|
+
ruby-version: ["2.4", "3.0", "3.4.1", "4.0.5"]
|
|
13
|
+
include:
|
|
14
|
+
- ruby-version: "head"
|
|
15
|
+
allow-failure: true
|
|
16
|
+
|
|
17
|
+
name: Specs - Ruby ${{ matrix.ruby-version }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
- name: Install libcurl
|
|
21
|
+
run: |
|
|
22
|
+
sudo apt-get update
|
|
23
|
+
sudo apt-get -y install libcurl4 libcurl4-openssl-dev
|
|
24
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
25
|
+
uses: ruby/setup-ruby@v1
|
|
26
|
+
with:
|
|
27
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
28
|
+
bundler-cache: true # 'bundle install' and cache
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: bundle exec rake
|
|
31
|
+
|
|
32
|
+
valgrind:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
name: Specs under valgrind memcheck
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v7
|
|
37
|
+
- name: Install libcurl and valgrind
|
|
38
|
+
run: |
|
|
39
|
+
sudo apt-get update
|
|
40
|
+
sudo apt-get -y install libcurl4-openssl-dev valgrind
|
|
41
|
+
- name: Set up Ruby
|
|
42
|
+
uses: ruby/setup-ruby@v1
|
|
43
|
+
with:
|
|
44
|
+
ruby-version: "3.4"
|
|
45
|
+
bundler-cache: true
|
|
46
|
+
- name: Compile the extension with debug info
|
|
47
|
+
run: bundle exec rake clobber compile
|
|
48
|
+
env:
|
|
49
|
+
PATRON_EXTRA_CFLAGS: "-g -O1"
|
|
50
|
+
- name: Run the specs under memcheck
|
|
51
|
+
# The ruby process itself is under valgrind (bundle exec only sets up the environment).
|
|
52
|
+
# Specs tagged :timing assert on wall-clock windows that valgrind's slowdown breaks.
|
|
53
|
+
run: |
|
|
54
|
+
bundle exec valgrind --error-exitcode=99 --leak-check=no --undef-value-errors=no \
|
|
55
|
+
--child-silent-after-fork=yes --num-callers=30 \
|
|
56
|
+
--suppressions=spec/support/valgrind.supp \
|
|
57
|
+
ruby -S rspec --tag ~timing
|
|
58
|
+
|
|
59
|
+
malloc-check:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
name: Specs with glibc malloc checking
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v7
|
|
64
|
+
- name: Install libcurl
|
|
65
|
+
run: |
|
|
66
|
+
sudo apt-get update
|
|
67
|
+
sudo apt-get -y install libcurl4-openssl-dev
|
|
68
|
+
- name: Set up Ruby
|
|
69
|
+
uses: ruby/setup-ruby@v1
|
|
70
|
+
with:
|
|
71
|
+
ruby-version: "3.4"
|
|
72
|
+
bundler-cache: true
|
|
73
|
+
- name: Run tests
|
|
74
|
+
# glibc aborts on heap corruption and fills freed memory with 0xA5, so a use-after-free
|
|
75
|
+
# shows up as garbage data rather than passing by luck
|
|
76
|
+
run: bundle exec rake
|
|
77
|
+
env:
|
|
78
|
+
MALLOC_CHECK_: "3"
|
|
79
|
+
MALLOC_PERTURB_: "165"
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
|
+
### Unreleased
|
|
2
|
+
|
|
3
|
+
### 0.14.0
|
|
4
|
+
|
|
5
|
+
* Expose libcurl's transfer timings (name lookup, connect, TLS, first byte, total) as `Response#timings`
|
|
6
|
+
* Add `ssl_cert`, `ssl_cert_type`, `ssl_key` and `ssl_key_password` for presenting a client certificate (mutual TLS). Thanks @lenage for the original contribution in #165
|
|
7
|
+
* Raise `Patron::ClientCertificateError` when the client certificate or its key can't be used
|
|
8
|
+
* Accept a `Pathname` for `cacert`
|
|
9
|
+
* Fix a request whose options fail to apply (for example an option of the wrong type) leaking its headers and open files into the next request on the same `Session`
|
|
10
|
+
* Fix the request body being read from freed memory when `upload_data` is not a String (its `#to_s` result was not kept alive during the transfer)
|
|
11
|
+
* Fix multipart field names and values pointing at temporary Strings, and binary multipart values being cut at the first NUL byte
|
|
12
|
+
* Fix an exception raised inside `progress_callback` (including `Timeout.timeout` and `Thread#raise`) unwinding through libcurl; it is now re-raised after the transfer is aborted
|
|
13
|
+
* Raise `Patron::Error` when a second thread uses a `Session` that is already performing a request, instead of corrupting both requests
|
|
14
|
+
* Fix the progress callback being declared with `size_t` arguments where libcurl passes `curl_off_t`
|
|
15
|
+
* Accept `download_byte_limit` above 2 GiB, and Floats for `dns_cache_timeout`, `low_speed_time` and `low_speed_limit`
|
|
16
|
+
* Fix `Session#unescape` cutting the result at a `%00`, and raise instead of crashing when libcurl fails to escape or unescape
|
|
17
|
+
* Reject file paths containing NUL bytes for cookie files, debug output and uploads
|
|
18
|
+
* Raise `NoMemoryError` instead of crashing later when libcurl cannot allocate a Session's handles
|
|
19
|
+
* Register the exception classes the extension looks up at load time with the GC, so heap compaction (`GC.compact`, `GC.auto_compact`) cannot move them out from under `rb_raise`
|
|
20
|
+
|
|
21
|
+
### 0.13.4
|
|
22
|
+
|
|
23
|
+
* Format README a bit better using code fences
|
|
24
|
+
* Depend explicitly on base64 for running tests
|
|
25
|
+
* Remove C code used for Ruby support below < 2.4 (GVL unlock etc.) and drop support for these versions
|
|
26
|
+
* In tests, close Tempfiles before allowing libcurl to use them (cookie jar)
|
|
27
|
+
* Fix incorrect ptr type in uses of `curl_easy_escape` / `_unescape`
|
|
28
|
+
* Define methods on `Session` as private instance methods from C instead of privatising them from Ruby
|
|
29
|
+
* Speed up the /slow endpoint test
|
|
30
|
+
* Remove use of `curl_easy_reset`. This should fix the bug with sessions resetting each other's state during `session_free`
|
|
31
|
+
|
|
1
32
|
### 0.13.3
|
|
2
33
|
|
|
3
|
-
*
|
|
34
|
+
* Run Puma test servers in separate processes instead of threads
|
|
4
35
|
|
|
5
36
|
### 0.13.2
|
|
6
37
|
|
data/Gemfile
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
2
|
|
|
3
|
-
#
|
|
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", '~> 5.6'
|
|
15
|
+
gem "rake-compiler"
|
|
16
|
+
gem "webrick", "~> 1.8"
|
|
17
|
+
gem "base64"
|
|
18
|
+
gem "ostruct"
|
data/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Patron
|
|
2
|
+
|
|
3
|
+
[](https://github.com/toland/patron/actions/workflows/ci.yml)
|
|
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,92 @@ 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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
+
```ruby
|
|
49
|
+
resp = sess.get("/foo/bar")
|
|
50
|
+
```
|
|
37
51
|
|
|
38
52
|
Requests return a Response object:
|
|
39
53
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
|
|
69
|
+
```ruby
|
|
70
|
+
sess.post("/foo/stuff", "some data", {"Content-Type" => "text/plain"})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
libcurl's transfer timings are available on the response, in seconds since the request started:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
resp = sess.get("/foo/bar")
|
|
77
|
+
resp.timings # => {namelookup: 0.001, connect: 0.012, appconnect: 0.045, pretransfer: 0.045,
|
|
78
|
+
# starttransfer: 0.210, redirect: 0.0, total: 0.214}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
To authenticate with a client certificate (mutual TLS), point Patron at a PKCS#12 bundle:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
sess.ssl_cert = "/path/to/client.p12"
|
|
85
|
+
sess.ssl_cert_type = "P12"
|
|
86
|
+
sess.ssl_key_password = "secret"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
or at a PEM certificate with a separate private key:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
sess.ssl_cert = "/path/to/client.pem"
|
|
93
|
+
sess.ssl_key = "/path/to/client_key.pem"
|
|
94
|
+
sess.ssl_key_password = "secret" # if the key is encrypted
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
If the certificate or key can't be loaded (wrong password, missing file) the request raises `Patron::ClientCertificateError`.
|
|
98
|
+
This depends on the TLS library libcurl is built against: with GnuTLS these surface as a plain `Patron::Error`.
|
|
99
|
+
Client certificates are tested with the OpenSSL and LibreSSL backends - libcurl's Secure Transport backend
|
|
100
|
+
can't load a PEM certificate from a file at all.
|
|
52
101
|
|
|
53
102
|
## Threading
|
|
54
103
|
|
|
@@ -58,16 +107,18 @@ for doing concurrent requests. However, the actual code that interacts with libC
|
|
|
58
107
|
so using multiple `Session` objects in different threads actually enables a high degree of parallelism.
|
|
59
108
|
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
109
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
110
|
+
```ruby
|
|
111
|
+
patron_pool = ConnectionPool.new(size: 5, timeout: 5) { Patron::Session.new }
|
|
112
|
+
patron_pool.with do |session|
|
|
113
|
+
session.get(...)
|
|
114
|
+
end
|
|
115
|
+
```
|
|
65
116
|
|
|
66
117
|
Sharing Session objects between requests will also allow you to benefit from persistent connections (connection reuse), see below.
|
|
67
118
|
|
|
68
119
|
## Persistent connections
|
|
69
120
|
|
|
70
|
-
Patron follows the libCURL guidelines on [connection reuse.](https://
|
|
121
|
+
Patron follows the libCURL guidelines on [connection reuse.](https://everything.curl.dev/libcurl/connectionreuse.html) If you create the Session
|
|
71
122
|
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
123
|
the same hostname/port/protocol the connection should get reused.
|
|
73
124
|
|
|
@@ -81,8 +132,10 @@ that are currently active in the process.
|
|
|
81
132
|
|
|
82
133
|
## Requirements
|
|
83
134
|
|
|
84
|
-
Patron
|
|
85
|
-
|
|
135
|
+
Patron 1.0 and up requires MRI Ruby 2.3 or newer. The 0.x versions support
|
|
136
|
+
Ruby 1.9.3 and these versions get tagged and developed on the `v0.x` branch.
|
|
137
|
+
|
|
138
|
+
A recent version of libCURL is required. We recommend at least 7.19.4 because
|
|
86
139
|
it [supports limiting the protocols](https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html),
|
|
87
140
|
and that is very important for security - especially if you follow redirects.
|
|
88
141
|
|
|
@@ -90,9 +143,6 @@ On OSX the provided libcurl is sufficient if you are not using fork+SSL combinat
|
|
|
90
143
|
You will have to install the libcurl development packages on Debian or Ubuntu. Other Linux systems are probably
|
|
91
144
|
similar. For Windows we do not have an established build instruction at the moment, unfortunately.
|
|
92
145
|
|
|
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
146
|
## Forking webservers on macOS and SSL
|
|
97
147
|
|
|
98
148
|
Currently, [an issue is at play](https://github.com/curl/curl/issues/788) with OSX builds of `curl` which use
|
data/ext/patron/extconf.rb
CHANGED
|
@@ -19,9 +19,12 @@ if CONFIG['CC'] =~ /gcc/
|
|
|
19
19
|
$CFLAGS << ' -pedantic -Wall'
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
$
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
if CONFIG['CC'] =~ /clang/
|
|
23
|
+
$CFLAGS << ' -pedantic -Wall -Wno-void-pointer-to-enum-cast'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Lets CI build with debug info for valgrind without editing this file, like so:
|
|
27
|
+
# PATRON_EXTRA_CFLAGS="-g -O1" rake compile
|
|
28
|
+
$CFLAGS << ' ' << ENV['PATRON_EXTRA_CFLAGS'] if ENV['PATRON_EXTRA_CFLAGS']
|
|
26
29
|
|
|
27
30
|
create_makefile 'patron/session_ext'
|