patron 0.14.0 → 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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -0
  3. data/README.md +83 -49
  4. data/ext/patron/extconf.rb +54 -23
  5. data/ext/patron/session_ext.c +1044 -923
  6. data/lib/patron/compat.rb +57 -0
  7. data/lib/patron/error.rb +21 -13
  8. data/lib/patron/header_parser.rb +27 -25
  9. data/lib/patron/request.rb +98 -144
  10. data/lib/patron/response.rb +28 -24
  11. data/lib/patron/response_decoding.rb +60 -67
  12. data/lib/patron/session.rb +114 -108
  13. data/lib/patron/util.rb +14 -12
  14. data/lib/patron/version.rb +3 -1
  15. data/lib/patron.rb +9 -15
  16. metadata +12 -58
  17. data/.autotest +0 -15
  18. data/.github/dependabot.yml +0 -6
  19. data/.github/workflows/ci.yml +0 -79
  20. data/.gitignore +0 -10
  21. data/.rspec +0 -0
  22. data/.travis.yml +0 -13
  23. data/.yardopts +0 -1
  24. data/Gemfile +0 -18
  25. data/Rakefile +0 -31
  26. data/ext/patron/.gitignore +0 -4
  27. data/ext/patron/membuffer.c +0 -85
  28. data/ext/patron/membuffer.h +0 -78
  29. data/ext/patron/sglib.h +0 -1952
  30. data/lib/patron/proxy_type.rb +0 -11
  31. data/patron.gemspec +0 -39
  32. data/pic.png +0 -0
  33. data/script/console +0 -11
  34. data/script/generate_client_certs +0 -65
  35. data/script/test_server +0 -7
  36. data/spec/header_parser_spec.rb +0 -101
  37. data/spec/patron_spec.rb +0 -28
  38. data/spec/request_spec.rb +0 -89
  39. data/spec/response_spec.rb +0 -145
  40. data/spec/sample_response_headers/headers_wetransfer.txt +0 -18
  41. data/spec/sample_response_headers/headers_wetransfer_with_proxy_status.txt +0 -20
  42. data/spec/sample_response_headers/headers_wetransfer_with_redirect.txt +0 -24
  43. data/spec/sample_response_headers/headers_with_set_cookie.txt +0 -9
  44. data/spec/sample_response_headers/sample_http2_header.txt +0 -16
  45. data/spec/sample_response_headers/webmock_headers_without_trailing_crlf.txt +0 -15
  46. data/spec/session_spec.rb +0 -865
  47. data/spec/session_ssl_spec.rb +0 -431
  48. data/spec/spec_helper.rb +0 -40
  49. data/spec/support/certs/cacert.pem +0 -36
  50. data/spec/support/certs/client.p12 +0 -0
  51. data/spec/support/certs/client_ca.pem +0 -20
  52. data/spec/support/certs/client_cert.pem +0 -20
  53. data/spec/support/certs/client_key.pem +0 -30
  54. data/spec/support/certs/privkey.pem +0 -51
  55. data/spec/support/certs/rogue_client.p12 +0 -0
  56. data/spec/support/config.ru +0 -153
  57. data/spec/support/fork.rb +0 -70
  58. data/spec/support/raw_request_capture.rb +0 -93
  59. data/spec/support/test_server.rb +0 -29
  60. data/spec/support/valgrind.supp +0 -2
  61. data/spec/util_spec.rb +0 -71
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 355b38921e6368ee1ad7815cd78ad9135630b10c82d60365978747e4189be390
4
- data.tar.gz: efbebf2f2e6393e93411d5583c9062b3170e0bf4e30ae370d1e998110b19c3c3
3
+ metadata.gz: 8f1026343c941740b978f220122296a7accd4de8770b18b317cdb6310e86dd5e
4
+ data.tar.gz: 0b90aed0ce0f16d4ae6b66ad0f44131b69e65b06d9e0e5e516d18c209b464160
5
5
  SHA512:
6
- metadata.gz: 760418bb81862c742f9873caa2513a6ed60093a084145b77e9f21996f041b0e848a0a6914e0248c0dca806bc5313c59eed68afd2546c828218a2e0aa52734ecf
7
- data.tar.gz: 77510952d581ea162ef55e93260fc8188e58b5f919dd97912c6d7b40863c94e2c517027c6de5fe2b5041431fb9645c85ad674b08f37ce7e2fcdb37b4f16f2da2
6
+ metadata.gz: cce780fab9bb4444992709dc18e2222bcb6e25fa03987b903270347e0a651080fe9024fa848da519b70a1511f429a4199f7e62425f554e4fb12b3bce198e31e7
7
+ data.tar.gz: d49a557d56dc10e57bc362f1af616eb779333da820c2899c4bb958347317d3bcc6eb171f8090ca03c2aae9ed94c5a53039e5c455206bb1372d743f6d029b97c3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,82 @@
1
1
  ### Unreleased
2
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
+
3
80
  ### 0.14.0
4
81
 
5
82
  * Expose libcurl's transfer timings (name lookup, connect, TLS, first byte, total) as `Response#timings`
data/README.md CHANGED
@@ -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({ :timeout => 10,
26
- :base_url => 'http://myserver.com:9900',
27
- :headers => {'User-Agent' => 'myapp/1.0'} } )
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 debug log:
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,13 +58,18 @@ if resp.status < 400
57
58
  end
58
59
  ```
59
60
 
60
- The GET, HEAD, PUT, POST and DELETE operations are all supported.
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
@@ -101,11 +107,11 @@ can't load a PEM certificate from a file at all.
101
107
 
102
108
  ## Threading
103
109
 
104
- By itself, the `Patron::Session` objects are not thread safe (each `Session` holds a single `curl_state` pointer
105
- from initialization to garbage collection). At this time, Patron has no support for `curl_multi_*` family of functions
106
- for doing concurrent requests. However, the actual code that interacts with libCURL does unlock the RVM GIL,
107
- so using multiple `Session` objects in different threads actually enables a high degree of parallelism.
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.
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.
109
115
 
110
116
  ```ruby
111
117
  patron_pool = ConnectionPool.new(size: 5, timeout: 5) { Patron::Session.new }
@@ -118,62 +124,90 @@ Sharing Session objects between requests will also allow you to benefit from per
118
124
 
119
125
  ## Persistent connections
120
126
 
121
- Patron follows the libCURL guidelines on [connection reuse.](https://everything.curl.dev/libcurl/connectionreuse.html) If you create the Session
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
123
- the same hostname/port/protocol the connection should get reused.
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)
124
131
 
125
- ## Performance with parallel requests
132
+ ## Interrupting requests
126
133
 
127
- When performing the libCURL request, Patron goes out of it's way to unlock the GVL (global VM lock) to allow other threads to be scheduled
128
- in parallel. The GVL is going to be released when the libCURL request starts, and will then be shortly re-acquired to provide the progress
129
- callback - if the callback has been configured, and then released again until the libCURL request has been performed and the response has
130
- been read in full. This allows one to execute multiple libCURL requests in parallel, as well as perform other activities on other MRI threads
131
- that are currently active in the process.
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.
132
139
 
133
- ## Requirements
140
+ ## Fibers and the async gem
134
141
 
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.
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:
137
145
 
138
- A recent version of libCURL is required. We recommend at least 7.19.4 because
139
- it [supports limiting the protocols](https://curl.haxx.se/libcurl/c/CURLOPT_PROTOCOLS.html),
140
- and that is very important for security - especially if you follow redirects.
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
+ ```
141
151
 
142
- On OSX the provided libcurl is sufficient if you are not using fork+SSL combination (see below).
143
- You will have to install the libcurl development packages on Debian or Ubuntu. Other Linux systems are probably
144
- 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.
145
154
 
146
- ## Forking webservers on macOS and SSL
155
+ ## Ractors
147
156
 
148
- Currently, [an issue is at play](https://github.com/curl/curl/issues/788) with OSX builds of `curl` which use
149
- Apple's SecureTransport. Such builds (which Patron is linking to), are causing segfaults when performing HTTPS
150
- requests in forked subprocesses. If you need to check whether your system is affected,
151
- run the Patron test suite by performing
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:
152
161
 
153
- $ bundle install && bundle exec rspec
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
154
190
 
155
- in the Patron install directory. Most default curl configurations on OSX (both
156
- the Apple-shipped version and the version available via Homebrew) are linked to
157
- SecureTransport and are likely to be affected. This issue may also manifest in
158
- forking webserver implementations (such as Unicorn or Passenger) and in forking
159
- job execution engines (such as resque), so even though you may not be using
160
- `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.
161
193
 
162
- To circumvent the issue, you need to build `curl` with OpenSSL via homebrew.
163
- When doing so, `curl` will use openssl as it's SSL driver. You also need to
164
- change the Patron compile flag:
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:
165
198
 
199
+ $ gem install patron -- --with-curl-dir=$(brew --prefix curl)
166
200
 
167
- $ brew install curl-openssl && \
168
- gem install patron -- --with-curl-config=/usr/local/opt/curl-openssl/bin/curl-config
201
+ or, for every Bundler-driven install:
169
202
 
170
- You can also save this parameter for all future Bundler-driven gem installs by
171
- setting this flag in Bundler proper:
203
+ $ bundle config build.patron --with-curl-dir=$(brew --prefix curl)
172
204
 
173
- $ bundle config build.patron --with-curl-config=/usr/local/opt/curl-openssl/bin/curl-config
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.
174
208
 
175
209
  ## Installation
176
210
 
177
- sudo gem install patron
211
+ gem install patron
178
212
 
179
213
  Copyright (c) 2008 The Hive
@@ -1,30 +1,61 @@
1
- require 'mkmf'
2
- require 'rbconfig'
3
-
4
- dir_config('curl')
5
- curl_config_path = with_config('curl-config') || find_executable('curl-config')
6
- if curl_config_path
7
- $CFLAGS << " " << `#{curl_config_path} --cflags`.strip
8
- $LIBS << " " << `#{curl_config_path} --libs`.strip
9
- elsif !have_library('curl') or !have_header('curl/curl.h')
10
- fail <<-EOM
11
- Can't find libcurl or curl/curl.h
12
-
13
- Try passing --with-curl-config, --with-curl-dir, or --with-curl-lib and --with-curl-include
14
- options to extconf.
15
- EOM
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
- if CONFIG['CC'] =~ /gcc/
19
- $CFLAGS << ' -pedantic -Wall'
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
- if CONFIG['CC'] =~ /clang/
23
- $CFLAGS << ' -pedantic -Wall -Wno-void-pointer-to-enum-cast'
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
- # 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']
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"]
29
60
 
30
- create_makefile 'patron/session_ext'
61
+ create_makefile "patron/session_ext"