tonic-rails 0.8.0 → 0.8.1

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: d6cc5ee93380e004201e88f63289b88dff01dd6cc6566c92a3d972b9c934150d
4
- data.tar.gz: 20a5a45adb019fa7275330b2d5b9f256a9f259765e1ffc83f4f1278902b38620
3
+ metadata.gz: cd7973d8a7b1550f1f483912639ec1cbec562297c3368c8751c45780e8472ec1
4
+ data.tar.gz: 385c74ea7100d36e7c128df6f1feda64fb7ac7c3b11a3905afa35d5ac275af26
5
5
  SHA512:
6
- metadata.gz: 452568660e7a6f19c16d77ff8d272691c0f54c7ebf67261e5199dab6ad59f6c1df89b4b8e0efab84748e089bd74f4e4c2bf4de7dd599e628de0fc11f6ce03922
7
- data.tar.gz: 52d5199c4d503c0792d84d254442a87cc25fc962b9af87561129c08c95974c48424c1a8914bd0cfcf664331d5c2f892c6cd8f31e507309bacb8c32b3ecc2fb53
6
+ metadata.gz: d491ec733022fca2b343ca3bb738273945c62614619266a1bccc8e8823f9f377628a2e6b4fb9a4b9c367b623e4c523ace580e18e4094fd66b07f94b7b55a0b97
7
+ data.tar.gz: 58a0d85837cd8f68f063d934b029761ab4239d30b9e2807bed5638b798ac1b18d2efe3e88c20c255dbb5c18aa3a5fee79ba86a8056d785eced8690ce4a6d0c17
data/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ minor versions may contain breaking changes.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.1] - 2026-10-13
11
+
12
+ ### Fixed
13
+
14
+ - Client: a call whose deadline passed could raise `CANCELLED` ("Timeout
15
+ expired") instead of `DEADLINE_EXCEEDED` (#48), mostly on Windows.
16
+ tonic enforces the deadline too, and when its timer fired first the
17
+ call ended with its status. Calls now raise `DEADLINE_EXCEEDED`
18
+ whichever timer fires first.
19
+
10
20
  ## [0.8.0] - 2026-10-12
11
21
 
12
22
  ### Added
@@ -207,7 +217,8 @@ First public release.
207
217
  - Precompiled native gems for Linux (x86_64, aarch64; glibc and musl) and
208
218
  macOS (x86_64, arm64), Ruby 3.3 to 4.0.
209
219
 
210
- [Unreleased]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.8.0...HEAD
220
+ [Unreleased]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.8.1...HEAD
221
+ [0.8.1]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.8.0...v0.8.1
211
222
  [0.8.0]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.7.0...v0.8.0
212
223
  [0.7.0]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.6.0...v0.7.0
213
224
  [0.6.0]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.5.0...v0.6.0
data/Cargo.lock CHANGED
@@ -1060,7 +1060,7 @@ dependencies = [
1060
1060
 
1061
1061
  [[package]]
1062
1062
  name = "tonic_rails_native"
1063
- version = "0.8.0"
1063
+ version = "0.8.1"
1064
1064
  dependencies = [
1065
1065
  "axum",
1066
1066
  "base64",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "tonic_rails_native"
3
- version = "0.8.0"
3
+ version = "0.8.1"
4
4
  edition = "2024"
5
5
  rust-version = "1.88"
6
6
  publish = false
@@ -30,7 +30,7 @@ use tokio::task::JoinHandle;
30
30
  use tonic::codec::{Codec, DecodeBuf, Decoder, EncodeBuf, Encoder, Streaming};
31
31
  use tonic::metadata::MetadataMap;
32
32
  use tonic::transport::{Certificate, Channel, ClientTlsConfig, Endpoint, Identity};
33
- use tonic::{Code, Request, Status};
33
+ use tonic::{Code, Request, Status, TimeoutExpired};
34
34
 
35
35
  use crate::gvl::{Cancel, Wait, block_on, recv_blocking, without_gvl};
36
36
  use crate::sync::Mutex;
@@ -218,13 +218,22 @@ async fn with_timeout(call: impl Future<Output = Outcome>, timeout: Option<Durat
218
218
  let started = tokio::time::Instant::now();
219
219
  let outcome = tokio::time::timeout(timeout, call).await;
220
220
  match outcome {
221
- // tonic's channel enforces the grpc-timeout header too, but ends
222
- // the call with its own status.
223
- Ok(outcome) if !(outcome.local && started.elapsed() >= timeout) => outcome,
221
+ Ok(outcome) if !(outcome.local && started.elapsed() >= timeout) && !timeout_expired(&outcome) => {
222
+ outcome
223
+ }
224
224
  _ => Outcome::error(Code::DeadlineExceeded, "deadline exceeded"),
225
225
  }
226
226
  }
227
227
 
228
+ /// tonic's channel enforces the grpc-timeout header too, and when its timer
229
+ /// fires before ours it ends the call with CANCELLED "Timeout expired", with
230
+ /// no source to mark it local. (A tonic server enforcing the same deadline
231
+ /// sends that status too.) Coarse timers, like Windows' 15 ms ticks, let
232
+ /// it fire first even though ours started earlier (#48).
233
+ fn timeout_expired(outcome: &Outcome) -> bool {
234
+ outcome.code == Code::Cancelled && outcome.message == TimeoutExpired(()).to_string()
235
+ }
236
+
228
237
  /// What every call needs: the method, one request message and its metadata.
229
238
  struct CallArgs {
230
239
  channel: Channel,
@@ -663,3 +672,42 @@ fn append_causes(mut message: String, mut source: Option<&dyn std::error::Error>
663
672
  }
664
673
  message
665
674
  }
675
+
676
+ #[cfg(test)]
677
+ mod tests {
678
+ use super::*;
679
+
680
+ fn run(call: impl Future<Output = Outcome>, timeout: Option<Duration>) -> Outcome {
681
+ let runtime = Builder::new_current_thread().enable_time().build().unwrap();
682
+ runtime.block_on(with_timeout(call, timeout))
683
+ }
684
+
685
+ async fn status(status: Status) -> Outcome {
686
+ Outcome::status(status, None)
687
+ }
688
+
689
+ #[test]
690
+ fn tonics_timeout_is_a_deadline_however_early_it_fires() {
691
+ let outcome = run(
692
+ status(Status::cancelled("Timeout expired")),
693
+ Some(Duration::from_secs(60)),
694
+ );
695
+ assert_eq!(outcome.code, Code::DeadlineExceeded);
696
+ }
697
+
698
+ #[test]
699
+ fn other_cancellations_stay_cancelled() {
700
+ let outcome = run(
701
+ status(Status::cancelled("client went away")),
702
+ Some(Duration::from_secs(60)),
703
+ );
704
+ assert_eq!(outcome.code, Code::Cancelled);
705
+ assert_eq!(outcome.message, "client went away");
706
+ }
707
+
708
+ #[test]
709
+ fn our_timer_is_a_deadline() {
710
+ let outcome = run(std::future::pending(), Some(Duration::from_millis(10)));
711
+ assert_eq!(outcome.code, Code::DeadlineExceeded);
712
+ }
713
+ }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Tonic
4
4
  module Rails
5
- VERSION = "0.8.0"
5
+ VERSION = "0.8.1"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tonic-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Tanous