patron 0.13.4 → 1.0.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/CHANGELOG.md +97 -0
- data/README.md +113 -50
- data/ext/patron/extconf.rb +55 -20
- data/ext/patron/session_ext.c +1092 -761
- data/lib/patron/compat.rb +57 -0
- data/lib/patron/error.rb +25 -13
- data/lib/patron/header_parser.rb +27 -25
- data/lib/patron/request.rb +98 -142
- data/lib/patron/response.rb +33 -24
- data/lib/patron/response_decoding.rb +60 -67
- data/lib/patron/session.rb +127 -104
- data/lib/patron/util.rb +14 -12
- data/lib/patron/version.rb +3 -1
- data/lib/patron.rb +9 -15
- metadata +12 -49
- data/.autotest +0 -15
- data/.github/workflows/ci.yml +0 -26
- data/.gitignore +0 -10
- data/.rspec +0 -0
- data/.travis.yml +0 -13
- data/.yardopts +0 -1
- data/Gemfile +0 -17
- data/Rakefile +0 -31
- data/ext/patron/.gitignore +0 -4
- data/ext/patron/membuffer.c +0 -85
- data/ext/patron/membuffer.h +0 -78
- data/ext/patron/sglib.h +0 -1952
- data/lib/patron/proxy_type.rb +0 -11
- data/patron.gemspec +0 -38
- data/pic.png +0 -0
- data/script/console +0 -11
- data/script/test_server +0 -7
- data/spec/header_parser_spec.rb +0 -101
- data/spec/patron_spec.rb +0 -28
- data/spec/request_spec.rb +0 -77
- data/spec/response_spec.rb +0 -145
- data/spec/sample_response_headers/headers_wetransfer.txt +0 -18
- data/spec/sample_response_headers/headers_wetransfer_with_proxy_status.txt +0 -20
- data/spec/sample_response_headers/headers_wetransfer_with_redirect.txt +0 -24
- data/spec/sample_response_headers/headers_with_set_cookie.txt +0 -9
- data/spec/sample_response_headers/sample_http2_header.txt +0 -16
- data/spec/sample_response_headers/webmock_headers_without_trailing_crlf.txt +0 -15
- data/spec/session_spec.rb +0 -698
- data/spec/session_ssl_spec.rb +0 -313
- data/spec/spec_helper.rb +0 -34
- data/spec/support/certs/cacert.pem +0 -36
- data/spec/support/certs/privkey.pem +0 -51
- data/spec/support/config.ru +0 -146
- data/spec/support/fork.rb +0 -70
- data/spec/support/test_server.rb +0 -26
- data/spec/util_spec.rb +0 -71
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8f1026343c941740b978f220122296a7accd4de8770b18b317cdb6310e86dd5e
|
|
4
|
+
data.tar.gz: 0b90aed0ce0f16d4ae6b66ad0f44131b69e65b06d9e0e5e516d18c209b464160
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cce780fab9bb4444992709dc18e2222bcb6e25fa03987b903270347e0a651080fe9024fa848da519b70a1511f429a4199f7e62425f554e4fb12b3bce198e31e7
|
|
7
|
+
data.tar.gz: d49a557d56dc10e57bc362f1af616eb779333da820c2899c4bb958347317d3bcc6eb171f8090ca03c2aae9ed94c5a53039e5c455206bb1372d743f6d029b97c3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,100 @@
|
|
|
1
|
+
### Unreleased
|
|
2
|
+
|
|
3
|
+
### 1.0.0
|
|
4
|
+
|
|
5
|
+
Patron 1.0 is a rewrite of the C extension for modern Ruby and modern libcurl. It follows Ivo Anjo's
|
|
6
|
+
guide ["Tips for updating Ruby C extensions for efficiently using Ruby 4 VM APIs"](https://github.com/ruby/ruby/blob/master/doc/efficient_extensions.md),
|
|
7
|
+
which has since been merged into the Ruby documentation, and his post
|
|
8
|
+
["Updating a C extension for a modern VM"](https://ivoanjo.me/blog/2026/09/23/updating-a-c-extension-for-a-modern-vm/).
|
|
9
|
+
It requires **Ruby 3.3+** and **libcurl 7.81+**.
|
|
10
|
+
|
|
11
|
+
**Transfers**
|
|
12
|
+
|
|
13
|
+
* A `Session` used inside a Fiber scheduler (`Async { }`, Falcon) yields to the scheduler while libcurl
|
|
14
|
+
waits for the network instead of blocking the thread, so requests from several fibers overlap and
|
|
15
|
+
`Async::Task#stop` / `with_timeout` interrupt a request cleanly. No API change, nothing to configure
|
|
16
|
+
|
|
17
|
+
* Requests are driven through libcurl's multi interface: every `Session` owns a multi handle with its own
|
|
18
|
+
connection cache (connection pooling, #138), `curl_multi_perform` runs with the GVL held and only
|
|
19
|
+
`curl_multi_poll` releases it, and `curl_multi_wakeup` is the unblocking function. `Thread#kill`,
|
|
20
|
+
`Thread#raise`, `Timeout.timeout`, signals and interpreter exit now interrupt a request immediately
|
|
21
|
+
in any phase of the transfer, including DNS resolution and connect, instead of at the next progress tick
|
|
22
|
+
* `Session#interrupt` aborts the request in flight from another thread immediately and raises `Patron::Aborted`
|
|
23
|
+
* Using one `Session` from two threads at once raises `Patron::Error` instead of corrupting memory
|
|
24
|
+
* Response headers and bodies are accumulated directly into Ruby Strings, no more intermediate C buffer and copy
|
|
25
|
+
* Multipart requests use libcurl's `curl_mime` API: field values are binary-safe, names and values are copied,
|
|
26
|
+
and multipart `PUT`/`PATCH` work too
|
|
27
|
+
* `download_byte_limit` raises `Patron::DownloadLimitExceeded` (a subclass of `Patron::Aborted`)
|
|
28
|
+
* Exceptions raised inside a `progress_callback` propagate out of the request after the transfer is cleaned up;
|
|
29
|
+
previously they unwound through libcurl's frames. Returning `false` from the callback aborts the request
|
|
30
|
+
* Every `Patron::Error` raised for a transfer carries the libcurl code as `#curl_code`
|
|
31
|
+
* A forked child no longer reuses the parent's libcurl handles and connections
|
|
32
|
+
* Cookies collected with `handle_cookies` (and TLS sessions) are kept across requests on the same
|
|
33
|
+
`Session`, not only across the redirects of one request
|
|
34
|
+
* `enable_debug` accepts an `IO` as well as a path, writes libcurl's verbose output through Ruby, and
|
|
35
|
+
never clobbers the process' stderr
|
|
36
|
+
|
|
37
|
+
**Memory and VM**
|
|
38
|
+
|
|
39
|
+
* The `Session` TypedData is write-barrier protected and compaction-aware; the request body is pinned
|
|
40
|
+
only while libcurl reads it; memory size is reported to `ObjectSpace`
|
|
41
|
+
* The extension declares itself Ractor-safe: Patron can be used from non-main Ractors. A `Session`
|
|
42
|
+
belongs to the Ractor that created it (Ruby refuses to copy, move or share one); `Response` and
|
|
43
|
+
`Request` objects can be made shareable, and a frozen `Response` still decodes its body. The 0.x
|
|
44
|
+
compatibility warnings are tracked per Ractor. Covered by specs on Ruby 4.0+
|
|
45
|
+
* Statically held exception classes are registered with the GC (they could dangle under compaction before)
|
|
46
|
+
* The global list of live sessions, the `at_exit` hook and the bundled `sglib.h` are gone
|
|
47
|
+
|
|
48
|
+
**Ruby API**
|
|
49
|
+
|
|
50
|
+
* `Patron::Request` keeps its setters (build one, change the URL, send it again) and also takes keyword
|
|
51
|
+
arguments; any HTTP token is accepted as a verb (#180), verbs libcurl does not know natively are sent as
|
|
52
|
+
custom requests, and `Marshal` now covers every attribute instead of 22 of 31
|
|
53
|
+
* `Session.new` takes keyword arguments
|
|
54
|
+
* `Response.new` takes keyword arguments (`url:`, `status:`, `redirect_count:`, `header_data:`, `body:`,
|
|
55
|
+
`default_charset:`, `timings:`); a custom `response_class` must accept the same
|
|
56
|
+
* `default_response_charset` is now honoured as the fallback for `Response#charset`
|
|
57
|
+
* `Response#body` is no longer mutated by `decoded_body`/`inspectable_body`
|
|
58
|
+
* `Patron.libcurl_features` reports the runtime libcurl version, TLS backend and feature flags
|
|
59
|
+
* `Patron::Request::SSL_VERSIONS` and `HTTP_VERSIONS` list the accepted version names; `"HTTPv3"` is accepted
|
|
60
|
+
* `Patron::ProxyType::HTTPS` added
|
|
61
|
+
* `base_url` and the request path are joined as URLs, not with `File.join`
|
|
62
|
+
* The 0.x positional forms of `Response.new`, `Session.new` and `Session#request` still work in 1.0 with a
|
|
63
|
+
deprecation warning (see `lib/patron/compat.rb` and #209); they go away in 1.1
|
|
64
|
+
|
|
65
|
+
**Removed**
|
|
66
|
+
|
|
67
|
+
* `Session#reset` (it was an alias of `interrupt`)
|
|
68
|
+
* `ssl_version` values `"SSLv2"` and `"SSLv3"` (refused by every supported libcurl anyway)
|
|
69
|
+
* Positional arguments to `Response.new`; `Request::READER_VARS`, `WRITER_VARS` and `VALID_ACTIONS` (see `Request::ATTRIBUTES`)
|
|
70
|
+
* Ruby < 3.3, libcurl < 7.81, and the `#if LIBCURL_VERSION_NUM` compatibility branches for libcurl 7.19 through 7.6x
|
|
71
|
+
|
|
72
|
+
**Build, packaging and tests**
|
|
73
|
+
|
|
74
|
+
* Require Ruby 3.3 or newer and libcurl 7.81.0 or newer
|
|
75
|
+
* Build with pkg-config when available; `--with-curl-dir` now takes precedence over a `curl-config` found on `PATH`
|
|
76
|
+
* Remove the dead `patron/proxy_type.rb` file; the `Patron::ProxyType` constants were always defined by the extension
|
|
77
|
+
* Stop shipping test fixtures and CI configuration inside the gem
|
|
78
|
+
* Test suite: Puma 6 and Rack 3, JSON request readback, configurable ports (`PATRON_TEST_HTTP_PORT` and friends), `PATRON_GC_STRESS=1` for GC and compaction stress runs
|
|
79
|
+
|
|
80
|
+
### 0.14.0
|
|
81
|
+
|
|
82
|
+
* Expose libcurl's transfer timings (name lookup, connect, TLS, first byte, total) as `Response#timings`
|
|
83
|
+
* 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
|
|
84
|
+
* Raise `Patron::ClientCertificateError` when the client certificate or its key can't be used
|
|
85
|
+
* Accept a `Pathname` for `cacert`
|
|
86
|
+
* 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`
|
|
87
|
+
* 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)
|
|
88
|
+
* Fix multipart field names and values pointing at temporary Strings, and binary multipart values being cut at the first NUL byte
|
|
89
|
+
* 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
|
|
90
|
+
* Raise `Patron::Error` when a second thread uses a `Session` that is already performing a request, instead of corrupting both requests
|
|
91
|
+
* Fix the progress callback being declared with `size_t` arguments where libcurl passes `curl_off_t`
|
|
92
|
+
* Accept `download_byte_limit` above 2 GiB, and Floats for `dns_cache_timeout`, `low_speed_time` and `low_speed_limit`
|
|
93
|
+
* Fix `Session#unescape` cutting the result at a `%00`, and raise instead of crashing when libcurl fails to escape or unescape
|
|
94
|
+
* Reject file paths containing NUL bytes for cookie files, debug output and uploads
|
|
95
|
+
* Raise `NoMemoryError` instead of crashing later when libcurl cannot allocate a Session's handles
|
|
96
|
+
* 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`
|
|
97
|
+
|
|
1
98
|
### 0.13.4
|
|
2
99
|
|
|
3
100
|
* Format README a bit better using code fences
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Patron
|
|
2
2
|
|
|
3
|
-
[](https://github.com/toland/patron/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
5
|
Patron is a Ruby HTTP client library based on libcurl. It does not try to expose
|
|
6
6
|
the full "power" (read complexity) of libcurl but instead tries to provide a
|
|
@@ -22,9 +22,9 @@ sess.headers['User-Agent'] = 'myapp/1.0'
|
|
|
22
22
|
You can set options with a hash in the constructor:
|
|
23
23
|
|
|
24
24
|
```ruby
|
|
25
|
-
sess = Patron::Session.new(
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
sess = Patron::Session.new(timeout: 10,
|
|
26
|
+
base_url: 'http://myserver.com:9900',
|
|
27
|
+
headers: {'User-Agent' => 'myapp/1.0'})
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Or the set options in a block:
|
|
@@ -37,10 +37,11 @@ sess = Patron::Session.new do |patron|
|
|
|
37
37
|
end
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
Output
|
|
40
|
+
Output libcurl's verbose log to a file, to any IO, or to `$stderr` when called without arguments:
|
|
41
41
|
|
|
42
42
|
```ruby
|
|
43
43
|
sess.enable_debug "/tmp/patron.debug"
|
|
44
|
+
sess.enable_debug $stdout
|
|
44
45
|
```
|
|
45
46
|
|
|
46
47
|
The Session is used to make HTTP requests.
|
|
@@ -57,26 +58,60 @@ if resp.status < 400
|
|
|
57
58
|
end
|
|
58
59
|
```
|
|
59
60
|
|
|
60
|
-
The GET, HEAD, PUT, POST and DELETE operations
|
|
61
|
+
The GET, HEAD, PUT, PATCH, POST and DELETE operations have their own methods, and any other
|
|
62
|
+
HTTP verb can be sent through `request`:
|
|
61
63
|
|
|
62
64
|
```ruby
|
|
63
65
|
sess.put("/foo/baz", "some data")
|
|
64
66
|
sess.delete("/foo/baz")
|
|
67
|
+
sess.request(:propfind, "/foo", {"Depth" => "1"})
|
|
65
68
|
```
|
|
66
69
|
|
|
70
|
+
A failed transfer raises a subclass of `Patron::Error` (`Patron::TimeoutError`, `Patron::ConnectionFailed`,
|
|
71
|
+
`Patron::HostResolutionError` and so on). Every such error carries the libcurl error code as `curl_code`.
|
|
72
|
+
|
|
67
73
|
You can ship custom headers with a single request:
|
|
68
74
|
|
|
69
75
|
```ruby
|
|
70
76
|
sess.post("/foo/stuff", "some data", {"Content-Type" => "text/plain"})
|
|
71
77
|
```
|
|
72
78
|
|
|
79
|
+
libcurl's transfer timings are available on the response, in seconds since the request started:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
resp = sess.get("/foo/bar")
|
|
83
|
+
resp.timings # => {namelookup: 0.001, connect: 0.012, appconnect: 0.045, pretransfer: 0.045,
|
|
84
|
+
# starttransfer: 0.210, redirect: 0.0, total: 0.214}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
To authenticate with a client certificate (mutual TLS), point Patron at a PKCS#12 bundle:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
sess.ssl_cert = "/path/to/client.p12"
|
|
91
|
+
sess.ssl_cert_type = "P12"
|
|
92
|
+
sess.ssl_key_password = "secret"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
or at a PEM certificate with a separate private key:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
sess.ssl_cert = "/path/to/client.pem"
|
|
99
|
+
sess.ssl_key = "/path/to/client_key.pem"
|
|
100
|
+
sess.ssl_key_password = "secret" # if the key is encrypted
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If the certificate or key can't be loaded (wrong password, missing file) the request raises `Patron::ClientCertificateError`.
|
|
104
|
+
This depends on the TLS library libcurl is built against: with GnuTLS these surface as a plain `Patron::Error`.
|
|
105
|
+
Client certificates are tested with the OpenSSL and LibreSSL backends - libcurl's Secure Transport backend
|
|
106
|
+
can't load a PEM certificate from a file at all.
|
|
107
|
+
|
|
73
108
|
## Threading
|
|
74
109
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
For sharing a
|
|
110
|
+
A `Patron::Session` performs one request at a time and must not be shared between threads: each `Session`
|
|
111
|
+
owns its own libcurl handles and buffers, and using it from two threads at once raises `Patron::Error`.
|
|
112
|
+
Patron releases the GVL (the global VM lock) whenever it waits for the network, so using one `Session`
|
|
113
|
+
per thread gives you real parallelism.
|
|
114
|
+
For sharing a pool of sessions between threads we recommend the excellent [connection_pool](https://rubygems.org/gems/connection_pool) gem by Mike Perham.
|
|
80
115
|
|
|
81
116
|
```ruby
|
|
82
117
|
patron_pool = ConnectionPool.new(size: 5, timeout: 5) { Patron::Session.new }
|
|
@@ -89,62 +124,90 @@ Sharing Session objects between requests will also allow you to benefit from per
|
|
|
89
124
|
|
|
90
125
|
## Persistent connections
|
|
91
126
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
the same hostname/port/protocol the connection
|
|
127
|
+
Every `Session` owns a libcurl multi handle with its own connection cache, plus a share handle for
|
|
128
|
+
cookies, DNS entries and TLS sessions. If you create the Session once and use it for multiple requests
|
|
129
|
+
to the same hostname/port/protocol, the connection gets reused, following libcurl's rules on
|
|
130
|
+
[connection reuse.](https://everything.curl.dev/libcurl/connectionreuse.html)
|
|
95
131
|
|
|
96
|
-
##
|
|
132
|
+
## Interrupting requests
|
|
97
133
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
134
|
+
A request in flight can be aborted from another thread with `Session#interrupt`, after which the
|
|
135
|
+
request raises `Patron::Aborted`. `Thread#kill`, `Thread#raise`, `Timeout.timeout` and signals interrupt
|
|
136
|
+
a request the same way, immediately and in any phase of the transfer, including DNS resolution and
|
|
137
|
+
connecting. A `progress_callback` that returns `false` aborts the request as well, and an exception
|
|
138
|
+
raised inside the callback propagates out of the request once libcurl is done with it.
|
|
103
139
|
|
|
104
|
-
##
|
|
140
|
+
## Fibers and the async gem
|
|
105
141
|
|
|
106
|
-
|
|
107
|
-
|
|
142
|
+
When a Fiber scheduler is installed for the current thread (as inside `Async { }`), Patron hands the
|
|
143
|
+
wait for the network to the scheduler instead of blocking the thread, so requests from several fibers
|
|
144
|
+
overlap and other fibers keep running while one waits:
|
|
108
145
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
146
|
+
```ruby
|
|
147
|
+
Async do |task|
|
|
148
|
+
pages = urls.map { |url| task.async { Patron::Session.new(timeout: 10).get(url) } }.map(&:wait)
|
|
149
|
+
end
|
|
150
|
+
```
|
|
112
151
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
similar. For Windows we do not have an established build instruction at the moment, unfortunately.
|
|
152
|
+
Each fiber needs its own `Session`, just like each thread does. `Async::Task#stop` and
|
|
153
|
+
`Task#with_timeout` interrupt a request in flight the same way `Thread#kill` and `Timeout` do.
|
|
116
154
|
|
|
117
|
-
##
|
|
155
|
+
## Ractors
|
|
118
156
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
157
|
+
Patron works from non-main Ractors, so Ractors give you parallelism for the Ruby side of your
|
|
158
|
+
requests as well, not just for the waits. A `Session` belongs to the Ractor that created it: Ruby refuses
|
|
159
|
+
to copy, move or share one (`Ractor::Error`), so create the `Session` inside the Ractor and ship
|
|
160
|
+
`Response` objects out. A worker pool looks like this on Ruby 4.0:
|
|
123
161
|
|
|
124
|
-
|
|
162
|
+
```ruby
|
|
163
|
+
results = Ractor::Port.new
|
|
164
|
+
workers = 4.times.map do
|
|
165
|
+
Ractor.new(results) do |results|
|
|
166
|
+
session = Patron::Session.new(timeout: 10)
|
|
167
|
+
while (url = Ractor.receive)
|
|
168
|
+
results << Ractor.make_shareable(session.get(url))
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
urls.each_with_index { |url, i| workers[i % workers.size].send(url) }
|
|
173
|
+
responses = urls.size.times.map { results.receive }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`Ractor.make_shareable` on the `Response` is optional: it saves copying the body when the Response crosses
|
|
177
|
+
over, and a shareable (frozen) `Response` still decodes its body. A `Patron::Error` raised inside the
|
|
178
|
+
Ractor comes out of `Ractor#value` as the `cause` of a `Ractor::RemoteError`, `curl_code` and all.
|
|
179
|
+
`Patron::Request` objects can be made shareable and used from any Ractor too.
|
|
180
|
+
|
|
181
|
+
## How the GVL is handled
|
|
182
|
+
|
|
183
|
+
Patron drives libcurl through its multi interface. The part of libcurl that moves bytes
|
|
184
|
+
(`curl_multi_perform`) runs with the GVL held, which is what makes it safe to run your progress callback
|
|
185
|
+
and to write straight into Ruby Strings from inside libcurl. Whenever libcurl has nothing to do but wait
|
|
186
|
+
for the network, Patron releases the GVL (`curl_multi_poll`), so other Ruby threads run in parallel with
|
|
187
|
+
the transfer, and that wait is what Ruby interrupts when it needs the thread back.
|
|
188
|
+
|
|
189
|
+
## Requirements
|
|
125
190
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
SecureTransport and are likely to be affected. This issue may also manifest in
|
|
129
|
-
forking webserver implementations (such as Unicorn or Passenger) and in forking
|
|
130
|
-
job execution engines (such as resque), so even though you may not be using
|
|
131
|
-
`fork()` directly your server engine might be doing it for you.
|
|
191
|
+
Patron 1.0 and up requires Ruby 3.3 or newer and libcurl 7.81.0 or newer (Ubuntu 22.04 and up,
|
|
192
|
+
Debian 12 and up, any current macOS). The 0.x versions support older Rubies and stay on the `v0.x` branch.
|
|
132
193
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
194
|
+
You will have to install the libcurl development package on Debian or Ubuntu (`libcurl4-openssl-dev`),
|
|
195
|
+
Alpine (`curl-dev`) and the like. The build looks libcurl up through `pkg-config`, then `curl-config`,
|
|
196
|
+
then the default include and library paths. To build against a specific libcurl, for example
|
|
197
|
+
Homebrew's OpenSSL-backed one instead of the SecureTransport-backed one macOS ships:
|
|
136
198
|
|
|
199
|
+
$ gem install patron -- --with-curl-dir=$(brew --prefix curl)
|
|
137
200
|
|
|
138
|
-
|
|
139
|
-
gem install patron -- --with-curl-config=/usr/local/opt/curl-openssl/bin/curl-config
|
|
201
|
+
or, for every Bundler-driven install:
|
|
140
202
|
|
|
141
|
-
|
|
142
|
-
setting this flag in Bundler proper:
|
|
203
|
+
$ bundle config build.patron --with-curl-dir=$(brew --prefix curl)
|
|
143
204
|
|
|
144
|
-
|
|
205
|
+
The SecureTransport backend of the macOS system libcurl cannot load PEM client certificates,
|
|
206
|
+
and [older versions of it crash](https://github.com/curl/curl/issues/788) when doing HTTPS in a forked
|
|
207
|
+
subprocess, so the Homebrew libcurl is the better choice on macOS.
|
|
145
208
|
|
|
146
209
|
## Installation
|
|
147
210
|
|
|
148
|
-
|
|
211
|
+
gem install patron
|
|
149
212
|
|
|
150
213
|
Copyright (c) 2008 The Hive
|
data/ext/patron/extconf.rb
CHANGED
|
@@ -1,26 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "mkmf"
|
|
4
|
+
|
|
5
|
+
MIN_LIBCURL = "7.81.0"
|
|
6
|
+
MIN_LIBCURL_NUM = 0x075100
|
|
7
|
+
|
|
8
|
+
# An explicit --with-curl-dir (or -include/-lib) wins over anything on PATH, otherwise
|
|
9
|
+
# the SDK's curl-config on macOS silently overrides the Homebrew one the user asked for
|
|
10
|
+
explicit_dir = dir_config("curl").any?
|
|
11
|
+
unless explicit_dir
|
|
12
|
+
pkg_config("libcurl") ||
|
|
13
|
+
begin
|
|
14
|
+
curl_config = with_config("curl-config") || find_executable("curl-config")
|
|
15
|
+
if curl_config
|
|
16
|
+
$CFLAGS << " " << `#{curl_config} --cflags`.strip
|
|
17
|
+
$LIBS << " " << `#{curl_config} --libs`.strip
|
|
18
|
+
end
|
|
19
|
+
end
|
|
16
20
|
end
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
# pkg-config and curl-config put -lcurl in $LIBS already, so only fall back to have_library
|
|
23
|
+
# when linking does not work as-is - otherwise we get -lcurl twice and a linker warning
|
|
24
|
+
linked = have_header("curl/curl.h") &&
|
|
25
|
+
(have_func("curl_easy_init", "curl/curl.h") || have_library("curl", "curl_easy_init"))
|
|
26
|
+
unless linked
|
|
27
|
+
abort <<~MSG
|
|
28
|
+
Can't find libcurl or curl/curl.h.
|
|
29
|
+
|
|
30
|
+
Install the libcurl development package (libcurl4-openssl-dev, curl-dev, ...) or pass
|
|
31
|
+
--with-curl-dir=/path/to/curl (also --with-curl-include / --with-curl-lib,
|
|
32
|
+
or --with-curl-config=/path/to/curl-config) to extconf.
|
|
33
|
+
MSG
|
|
20
34
|
end
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
found_num = try_constant("LIBCURL_VERSION_NUM", "curl/curlver.h")
|
|
37
|
+
if found_num.nil? || found_num < MIN_LIBCURL_NUM
|
|
38
|
+
found = found_num ? "%d.%d.%d" % [found_num >> 16, (found_num >> 8) & 0xff, found_num & 0xff] : "an unknown version"
|
|
39
|
+
abort "Patron needs libcurl #{MIN_LIBCURL} or newer, found #{found}."
|
|
24
40
|
end
|
|
25
41
|
|
|
26
|
-
|
|
42
|
+
%w[
|
|
43
|
+
CURLOPT_PROTOCOLS_STR
|
|
44
|
+
CURL_HTTP_VERSION_3
|
|
45
|
+
CURLOPT_CA_CACHE_TIMEOUT
|
|
46
|
+
CURL_VERSION_THREADSAFE
|
|
47
|
+
CURLINFO_QUEUE_TIME_T
|
|
48
|
+
CURLINFO_POSTTRANSFER_TIME_T
|
|
49
|
+
].each { |const| have_const(const, "curl/curl.h") }
|
|
50
|
+
have_func("curl_easy_header", "curl/curl.h")
|
|
51
|
+
|
|
52
|
+
# Ruby's own warnflags already bring -Wall -Wextra; these two turn the warnings
|
|
53
|
+
# that hide real ABI mistakes into errors
|
|
54
|
+
append_cflags(%w[-Werror=implicit-function-declaration -Werror=incompatible-pointer-types])
|
|
55
|
+
append_cflags("-Werror") if enable_config("werror")
|
|
56
|
+
|
|
57
|
+
# For sanitizer builds in CI: plain CFLAGS gets clobbered by RbConfig, so use our own names
|
|
58
|
+
$CFLAGS << " " << ENV["PATRON_CFLAGS"] if ENV["PATRON_CFLAGS"]
|
|
59
|
+
$LDFLAGS << " " << ENV["PATRON_LDFLAGS"] if ENV["PATRON_LDFLAGS"]
|
|
60
|
+
|
|
61
|
+
create_makefile "patron/session_ext"
|