wreq-rb 0.6.1 → 0.6.2
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/Cargo.lock +1 -1
- data/ext/wreq_rb/Cargo.toml +4 -1
- data/ext/wreq_rb/src/client.rs +104 -14
- data/ext/wreq_rb/src/response.rs +28 -3
- data/lib/wreq-rb/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ce3d1d61f00a9a6585dcf3dedf9c32efe8bb25d99ab3ce332deee5de6c84ee4f
|
|
4
|
+
data.tar.gz: 97e22c094b0d0f28db1c0e65f94eb82eeca2f55bb1a96fa8e7f706d17a008df3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7ce21633e4394d4f4d6a3a71ad38708d98e9809f3d0910bf5e0f947adb2bdabbb59481bcd5a4c9096114fc74ffa035c2519e4460733a61c9a810220be3c6b7a3
|
|
7
|
+
data.tar.gz: 643d9f5411aaa343c165665a9a1de939102d2abe7396b5c07ff16e18456a0c5fce885ffe0118c57491ce850227117a5d5248ebda5d083c0b5c903020b0458978
|
data/Cargo.lock
CHANGED
data/ext/wreq_rb/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "wreq_rb"
|
|
3
|
-
version = "0.6.
|
|
3
|
+
version = "0.6.2"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
publish = false
|
|
6
6
|
|
|
@@ -31,6 +31,9 @@ serde_json = "1.0"
|
|
|
31
31
|
bytes = "1"
|
|
32
32
|
http = "1"
|
|
33
33
|
|
|
34
|
+
[dev-dependencies]
|
|
35
|
+
magnus = { version = "0.8", features = ["embed"] }
|
|
36
|
+
|
|
34
37
|
[target.'cfg(target_os = "linux")'.dependencies]
|
|
35
38
|
wreq = { path = "../../vendor/wreq", version = "=0.16.1", features = [
|
|
36
39
|
"prefix-symbols",
|
data/ext/wreq_rb/src/client.rs
CHANGED
|
@@ -47,7 +47,7 @@ fn runtime() -> &'static Runtime {
|
|
|
47
47
|
/// # Safety
|
|
48
48
|
/// The closure must NOT access any Ruby objects or call any Ruby C API.
|
|
49
49
|
/// Extract all data from Ruby before calling this, convert results after.
|
|
50
|
-
unsafe fn without_gvl<F, R>(f: F) -> R
|
|
50
|
+
unsafe fn without_gvl<F, R>(f: F) -> Result<R, magnus::Error>
|
|
51
51
|
where
|
|
52
52
|
F: FnOnce(CancellationToken) -> R,
|
|
53
53
|
{
|
|
@@ -89,19 +89,22 @@ where
|
|
|
89
89
|
let data_ptr = &mut data as *mut CallData<F, R> as *mut c_void;
|
|
90
90
|
|
|
91
91
|
unsafe {
|
|
92
|
-
rb_sys::
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
magnus::rb_sys::protect(|| {
|
|
93
|
+
rb_sys::rb_thread_call_without_gvl(
|
|
94
|
+
Some(call::<F, R>),
|
|
95
|
+
data_ptr,
|
|
96
|
+
Some(ubf::<F, R>),
|
|
97
|
+
data_ptr,
|
|
98
|
+
);
|
|
99
|
+
0
|
|
100
|
+
})?;
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
if let Some(payload) = data.panic_payload {
|
|
101
104
|
panic::resume_unwind(payload);
|
|
102
105
|
}
|
|
103
106
|
|
|
104
|
-
data.result.unwrap()
|
|
107
|
+
Ok(data.result.unwrap())
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
/// Collected response data as pure Rust types (no Ruby objects).
|
|
@@ -288,12 +291,13 @@ impl Client {
|
|
|
288
291
|
builder = builder.default_headers(hmap);
|
|
289
292
|
}
|
|
290
293
|
|
|
291
|
-
|
|
292
|
-
|
|
294
|
+
let timeout = hash_get_float(&opts, "timeout")?;
|
|
295
|
+
if let Some(timeout) = timeout {
|
|
296
|
+
builder = builder.timeout(Duration::from_secs_f64(timeout));
|
|
293
297
|
}
|
|
294
298
|
|
|
295
|
-
if let Some(
|
|
296
|
-
builder = builder.connect_timeout(Duration::from_secs_f64(
|
|
299
|
+
if let Some(connect_timeout) = hash_get_float(&opts, "connect_timeout")?.or(timeout) {
|
|
300
|
+
builder = builder.connect_timeout(Duration::from_secs_f64(connect_timeout));
|
|
297
301
|
}
|
|
298
302
|
|
|
299
303
|
if let Some(t) = hash_get_float(&opts, "read_timeout")? {
|
|
@@ -489,7 +493,7 @@ impl Client {
|
|
|
489
493
|
}
|
|
490
494
|
})
|
|
491
495
|
})
|
|
492
|
-
}
|
|
496
|
+
}?;
|
|
493
497
|
|
|
494
498
|
let data = match outcome {
|
|
495
499
|
RequestOutcome::Ok(d) => d,
|
|
@@ -545,7 +549,7 @@ impl Client {
|
|
|
545
549
|
}
|
|
546
550
|
})
|
|
547
551
|
})
|
|
548
|
-
}
|
|
552
|
+
}?;
|
|
549
553
|
|
|
550
554
|
let items = match outcome {
|
|
551
555
|
BatchOutcome::Done(items) => items,
|
|
@@ -877,3 +881,89 @@ pub fn init(_ruby: &magnus::Ruby, module: &magnus::RModule) -> Result<(), magnus
|
|
|
877
881
|
|
|
878
882
|
Ok(())
|
|
879
883
|
}
|
|
884
|
+
|
|
885
|
+
#[cfg(test)]
|
|
886
|
+
mod tests {
|
|
887
|
+
use super::*;
|
|
888
|
+
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|
889
|
+
|
|
890
|
+
static READY: AtomicBool = AtomicBool::new(false);
|
|
891
|
+
static LIVE: AtomicUsize = AtomicUsize::new(0);
|
|
892
|
+
|
|
893
|
+
struct DropProbe;
|
|
894
|
+
|
|
895
|
+
impl DropProbe {
|
|
896
|
+
fn new() -> Self {
|
|
897
|
+
LIVE.fetch_add(1, Ordering::SeqCst);
|
|
898
|
+
Self
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
impl Drop for DropProbe {
|
|
903
|
+
fn drop(&mut self) {
|
|
904
|
+
LIVE.fetch_sub(1, Ordering::SeqCst);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
fn probe_ready() -> bool {
|
|
909
|
+
READY.load(Ordering::SeqCst)
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
fn drop_probe() -> Result<(), magnus::Error> {
|
|
913
|
+
let _caller_resource = DropProbe::new();
|
|
914
|
+
let result = unsafe {
|
|
915
|
+
without_gvl(|token| {
|
|
916
|
+
let _callback_resource = DropProbe::new();
|
|
917
|
+
READY.store(true, Ordering::SeqCst);
|
|
918
|
+
runtime().block_on(token.cancelled());
|
|
919
|
+
DropProbe::new()
|
|
920
|
+
})
|
|
921
|
+
}?;
|
|
922
|
+
drop(result);
|
|
923
|
+
Ok(())
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
#[test]
|
|
927
|
+
fn ruby_interrupts_drop_native_resources() {
|
|
928
|
+
let ruby = unsafe { magnus::embed::init() };
|
|
929
|
+
let result = unsafe { without_gvl(|_| DropProbe::new()) }.unwrap();
|
|
930
|
+
assert_eq!(LIVE.load(Ordering::SeqCst), 1);
|
|
931
|
+
drop(result);
|
|
932
|
+
assert_eq!(LIVE.load(Ordering::SeqCst), 0);
|
|
933
|
+
|
|
934
|
+
let panicked = panic::catch_unwind(|| unsafe {
|
|
935
|
+
without_gvl::<_, ()>(|_| {
|
|
936
|
+
let _resource = DropProbe::new();
|
|
937
|
+
panic!("probe panic");
|
|
938
|
+
})
|
|
939
|
+
});
|
|
940
|
+
assert!(panicked.is_err());
|
|
941
|
+
assert_eq!(LIVE.load(Ordering::SeqCst), 0);
|
|
942
|
+
|
|
943
|
+
ruby.define_global_function("wreq_drop_probe", function!(drop_probe, 0));
|
|
944
|
+
ruby.define_global_function("wreq_probe_ready", function!(probe_ready, 0));
|
|
945
|
+
|
|
946
|
+
for (interrupt, expected) in [
|
|
947
|
+
("worker.kill", None),
|
|
948
|
+
("worker.raise(RuntimeError, 'stop request')", Some("stop request")),
|
|
949
|
+
] {
|
|
950
|
+
READY.store(false, Ordering::SeqCst);
|
|
951
|
+
let result: Option<String> = ruby.eval(&format!(
|
|
952
|
+
"worker = Thread.new do
|
|
953
|
+
begin
|
|
954
|
+
wreq_drop_probe
|
|
955
|
+
'returned normally'
|
|
956
|
+
rescue RuntimeError => error
|
|
957
|
+
error.message
|
|
958
|
+
end
|
|
959
|
+
end
|
|
960
|
+
Thread.pass until wreq_probe_ready
|
|
961
|
+
{interrupt}
|
|
962
|
+
worker.value"
|
|
963
|
+
)).unwrap();
|
|
964
|
+
|
|
965
|
+
assert_eq!(result.as_deref(), expected);
|
|
966
|
+
assert_eq!(LIVE.load(Ordering::SeqCst), 0, "resources leaked after {interrupt}");
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
data/ext/wreq_rb/src/response.rs
CHANGED
|
@@ -5,7 +5,8 @@ use magnus::{
|
|
|
5
5
|
use crate::error::generic_error;
|
|
6
6
|
|
|
7
7
|
/// Wraps a wreq::Response in a Ruby-accessible type.
|
|
8
|
-
#[magnus::
|
|
8
|
+
#[derive(magnus::TypedData)]
|
|
9
|
+
#[magnus(class = "Wreq::Response", free_immediately, size)]
|
|
9
10
|
pub struct Response {
|
|
10
11
|
status: u16,
|
|
11
12
|
headers: Vec<(String, String)>,
|
|
@@ -26,7 +27,7 @@ impl Response {
|
|
|
26
27
|
content_length: Option<u64>,
|
|
27
28
|
transfer_size: Option<u64>,
|
|
28
29
|
) -> Self {
|
|
29
|
-
Self {
|
|
30
|
+
let response = Self {
|
|
30
31
|
status,
|
|
31
32
|
headers,
|
|
32
33
|
body,
|
|
@@ -34,7 +35,18 @@ impl Response {
|
|
|
34
35
|
version,
|
|
35
36
|
content_length,
|
|
36
37
|
transfer_size,
|
|
37
|
-
}
|
|
38
|
+
};
|
|
39
|
+
unsafe { Ruby::get_unchecked() }
|
|
40
|
+
.gc_adjust_memory_usage(response.heap_size() as isize);
|
|
41
|
+
response
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn heap_size(&self) -> usize {
|
|
45
|
+
self.body.capacity()
|
|
46
|
+
+ self.url.capacity()
|
|
47
|
+
+ self.version.capacity()
|
|
48
|
+
+ self.headers.capacity() * std::mem::size_of::<(String, String)>()
|
|
49
|
+
+ self.headers.iter().map(|(name, value)| name.capacity() + value.capacity()).sum::<usize>()
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
fn status(&self) -> u16 {
|
|
@@ -119,6 +131,19 @@ impl Response {
|
|
|
119
131
|
}
|
|
120
132
|
}
|
|
121
133
|
|
|
134
|
+
impl magnus::DataTypeFunctions for Response {
|
|
135
|
+
fn size(&self) -> usize {
|
|
136
|
+
std::mem::size_of::<Self>() + self.heap_size()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
impl Drop for Response {
|
|
141
|
+
fn drop(&mut self) {
|
|
142
|
+
unsafe { Ruby::get_unchecked() }
|
|
143
|
+
.gc_adjust_memory_usage(-(self.heap_size() as isize));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
122
147
|
pub fn init(ruby: &magnus::Ruby, module: &magnus::RModule) -> Result<(), magnus::Error> {
|
|
123
148
|
let class = module.define_class("Response", ruby.class_object())?;
|
|
124
149
|
class.define_method("status", method!(Response::status, 0))?;
|
data/lib/wreq-rb/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wreq-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yicheng Zhou
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-
|
|
12
|
+
date: 2026-09-13 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rb_sys
|