From 422fd4cc7226d49ff51dcf1c17d417e3ae931919 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 23 Aug 2026 14:04:17 +0200 Subject: [PATCH 1/4] deprecate Transport::async() --- CHANGELOG.md | 6 ++++++ src/Transport.php | 1 + 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 947a8f9..44de0ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] + +### Deprecated + +- `Innmind\HttpTransport\Transport::async()` + ## 9.2.0 - 2026-08-16 ### Changed diff --git a/src/Transport.php b/src/Transport.php index 055e26b..d59d35d 100644 --- a/src/Transport.php +++ b/src/Transport.php @@ -95,6 +95,7 @@ public static function logger( /** * @internal + * @deprecated * * @param callable(): void $heartbeat */ From 1d2753b7d587bb26194b7b851b20ebf2c74e2d50 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 23 Aug 2026 14:58:19 +0200 Subject: [PATCH 2/4] add Config::asAsync() --- CHANGELOG.md | 4 ++++ src/Config.php | 42 ++++++++++++++++++++++++++++++++++++++++++ src/Config/Async.php | 37 +++++++++++++++++++++++++++++++++++++ src/Transport/Curl.php | 18 +++++++++++++++--- 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 src/Config/Async.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 44de0ce..f81e4d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Internal `Innmind\HttpTransport\Config::asAsync()` + ### Deprecated - `Innmind\HttpTransport\Transport::async()` diff --git a/src/Config.php b/src/Config.php index 2b3b4c0..17bf9db 100644 --- a/src/Config.php +++ b/src/Config.php @@ -3,7 +3,13 @@ namespace Innmind\HttpTransport; +use Innmind\HttpTransport\Config\Async; +use Innmind\IO\IO; use Innmind\Url\Url; +use Innmind\Time\{ + Clock, + Halt, +}; use Innmind\Immutable\Maybe; /** @@ -14,11 +20,13 @@ final class Config /** * @param Maybe> $maxConcurrency * @param Maybe $proxy + * @param Maybe $async, */ private function __construct( private Maybe $maxConcurrency, private bool $verifySSL, private Maybe $proxy, + private Maybe $async, ) { } @@ -31,11 +39,14 @@ public static function new(): self $maxConcurrency = Maybe::nothing(); /** @var Maybe */ $proxy = Maybe::nothing(); + /** @var Maybe */ + $async = Maybe::nothing(); return new self( $maxConcurrency, true, $proxy, + $async, ); } @@ -49,6 +60,7 @@ public function limitConcurrencyTo(int $max): self Maybe::just($max), $this->verifySSL, $this->proxy, + $this->async, ); } @@ -63,6 +75,7 @@ public function disableSSLVerification(): self $this->maxConcurrency, false, $this->proxy, + $this->async, ); } @@ -73,6 +86,25 @@ public function throughProxy(Url $proxy): self $this->maxConcurrency, $this->verifySSL, Maybe::just($proxy), + $this->async, + ); + } + + #[\NoDiscard] + public function asAsync( + Clock $clock, + Halt $halt, + IO $io, + ): self { + return new self( + $this->maxConcurrency, + $this->verifySSL, + $this->proxy, + Maybe::just(new Async( + $clock, + $halt, + $io, + )), ); } @@ -103,4 +135,14 @@ public function proxy(): Maybe { return $this->proxy; } + + /** + * @internal + * + * @return Maybe + */ + public function async(): Maybe + { + return $this->async; + } } diff --git a/src/Config/Async.php b/src/Config/Async.php new file mode 100644 index 0000000..6ca0c92 --- /dev/null +++ b/src/Config/Async.php @@ -0,0 +1,37 @@ +clock; + } + + public function halt(): Halt + { + return $this->halt; + } + + public function io(): IO + { + return $this->io; + } +} diff --git a/src/Transport/Curl.php b/src/Transport/Curl.php index 610e72d..f7e7ec4 100644 --- a/src/Transport/Curl.php +++ b/src/Transport/Curl.php @@ -114,13 +114,25 @@ public function map(callable $map): self return new self( $config, $this->headerFactory, - $this->io, + $config->async()->match( + static fn($async) => $async->io(), + fn() => $this->io, + ), Concurrency::new($config->maxConcurrency()->match( static fn($max) => $max, static fn() => null, )), - $this->timeout, - $this->heartbeat, + $config->async()->match( + static fn() => Period::millisecond(10), // this is blocking the active task so it needs to be low + fn() => $this->timeout, + ), + $config + ->async() + ->map(static fn($async) => $async->halt()) + ->match( + static fn($halt) => static fn() => $halt(Period::millisecond(1))->unwrap(), // this allows to jump between tasks + fn() => $this->heartbeat, + ), !$config->verifySSL(), $config->proxy()->match( static fn($proxy) => $proxy, From 595d20bbe05a3c72e884492482bbbc28ea4497f0 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 23 Aug 2026 15:03:16 +0200 Subject: [PATCH 3/4] give access to the underlying config to each decorator --- src/Transport/CircuitBreaker.php | 10 ++++++++++ src/Transport/Curl.php | 9 +++++++++ src/Transport/ExponentialBackoff.php | 10 ++++++++++ src/Transport/FollowRedirections.php | 10 ++++++++++ src/Transport/Implementation.php | 6 ++++++ src/Transport/Logger.php | 10 ++++++++++ src/Transport/Via.php | 10 ++++++++++ 7 files changed, 65 insertions(+) diff --git a/src/Transport/CircuitBreaker.php b/src/Transport/CircuitBreaker.php index c527b1d..03f6588 100644 --- a/src/Transport/CircuitBreaker.php +++ b/src/Transport/CircuitBreaker.php @@ -4,6 +4,7 @@ namespace Innmind\HttpTransport\Transport; use Innmind\HttpTransport\{ + Config, Success, ServerError, ConnectionFailed, @@ -86,6 +87,15 @@ public function map(callable $map): self ); } + /** + * @psalm-mutation-free + */ + #[\Override] + public function config(): Config + { + return $this->fulfill->config(); + } + private function open( Request $request, ServerError|ConnectionFailed $error, diff --git a/src/Transport/Curl.php b/src/Transport/Curl.php index f7e7ec4..db1da72 100644 --- a/src/Transport/Curl.php +++ b/src/Transport/Curl.php @@ -140,4 +140,13 @@ public function map(callable $map): self ), ); } + + /** + * @psalm-mutation-free + */ + #[\Override] + public function config(): Config + { + return $this->config; + } } diff --git a/src/Transport/ExponentialBackoff.php b/src/Transport/ExponentialBackoff.php index 33a5162..55cb73a 100644 --- a/src/Transport/ExponentialBackoff.php +++ b/src/Transport/ExponentialBackoff.php @@ -4,6 +4,7 @@ namespace Innmind\HttpTransport\Transport; use Innmind\HttpTransport\{ + Config, Success, Information, Redirection, @@ -86,6 +87,15 @@ public function map(callable $map): self ); } + /** + * @psalm-mutation-free + */ + #[\Override] + public function config(): Config + { + return $this->fulfill->config(); + } + /** * @param Sequence $retries * diff --git a/src/Transport/FollowRedirections.php b/src/Transport/FollowRedirections.php index c628124..0dad84c 100644 --- a/src/Transport/FollowRedirections.php +++ b/src/Transport/FollowRedirections.php @@ -4,6 +4,7 @@ namespace Innmind\HttpTransport\Transport; use Innmind\HttpTransport\{ + Config, Success, Redirection, }; @@ -59,6 +60,15 @@ public function map(callable $map): self return self::of($this->fulfill->map($map)); } + /** + * @psalm-mutation-free + */ + #[\Override] + public function config(): Config + { + return $this->fulfill->config(); + } + /** * @param int<0, max> $hops * diff --git a/src/Transport/Implementation.php b/src/Transport/Implementation.php index bc50615..c07b600 100644 --- a/src/Transport/Implementation.php +++ b/src/Transport/Implementation.php @@ -36,4 +36,10 @@ public function __invoke(Request $request): Either; */ #[\NoDiscard] public function map(callable $map): self; + + /** + * @psalm-mutation-free + */ + #[\NoDiscard] + public function config(): Config; } diff --git a/src/Transport/Logger.php b/src/Transport/Logger.php index 4e0f08b..7b91f11 100644 --- a/src/Transport/Logger.php +++ b/src/Transport/Logger.php @@ -4,6 +4,7 @@ namespace Innmind\HttpTransport\Transport; use Innmind\HttpTransport\{ + Config, Success, Information, Redirection, @@ -62,6 +63,15 @@ public function map(callable $map): self ); } + /** + * @psalm-mutation-free + */ + #[\Override] + public function config(): Config + { + return $this->fulfill->config(); + } + private function logRequest(Request $request): string { $this->logger->debug( diff --git a/src/Transport/Via.php b/src/Transport/Via.php index ff2372a..59c4643 100644 --- a/src/Transport/Via.php +++ b/src/Transport/Via.php @@ -5,6 +5,7 @@ use Innmind\HttpTransport\Success; use Innmind\Http\Request; +use Innmind\HttpTransport\Config; use Innmind\Immutable\Either; /** @@ -44,4 +45,13 @@ public function map(callable $map): self { return $this; } + + /** + * @psalm-mutation-free + */ + #[\Override] + public function config(): Config + { + return Config::new(); + } } From 7ddc22a18feb3c15ee68736037eaa48629e7b58d Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Sun, 23 Aug 2026 15:08:12 +0200 Subject: [PATCH 4/4] reconfigure each decorator based on the new underlying configuration --- src/Transport/CircuitBreaker.php | 9 +++++++-- src/Transport/ExponentialBackoff.php | 9 +++++++-- src/Transport/Via.php | 11 ++++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Transport/CircuitBreaker.php b/src/Transport/CircuitBreaker.php index 03f6588..be6c4b7 100644 --- a/src/Transport/CircuitBreaker.php +++ b/src/Transport/CircuitBreaker.php @@ -79,9 +79,14 @@ public static function of( #[\Override] public function map(callable $map): self { + $fulfill = $this->fulfill->map($map); + return new self( - $this->fulfill->map($map), - $this->clock, + $fulfill, + $fulfill->config()->async()->match( + static fn($async) => $async->clock(), + fn() => $this->clock, + ), $this->delayBeforeRetry, Map::of(), ); diff --git a/src/Transport/ExponentialBackoff.php b/src/Transport/ExponentialBackoff.php index 55cb73a..d962bf3 100644 --- a/src/Transport/ExponentialBackoff.php +++ b/src/Transport/ExponentialBackoff.php @@ -81,9 +81,14 @@ public static function of( #[\Override] public function map(callable $map): self { + $fulfill = $this->fulfill->map($map); + return self::of( - $this->fulfill->map($map), - $this->halt, + $fulfill, + $fulfill->config()->async()->match( + static fn($async) => $async->halt(), + fn() => $this->halt, + ), ); } diff --git a/src/Transport/Via.php b/src/Transport/Via.php index 59c4643..2a7670e 100644 --- a/src/Transport/Via.php +++ b/src/Transport/Via.php @@ -19,6 +19,7 @@ final class Via implements Implementation */ private function __construct( private \Closure $fulfill, + private ?Config $config, ) { } @@ -34,7 +35,7 @@ public function __invoke(Request $request): Either public static function of(callable $fulfill): self { // todo support exposing a Config to the callable ? - return new self(\Closure::fromCallable($fulfill)); + return new self(\Closure::fromCallable($fulfill), null); } /** @@ -43,7 +44,11 @@ public static function of(callable $fulfill): self #[\Override] public function map(callable $map): self { - return $this; + /** @psalm-suppress ImpureFunctionCall */ + return new self( + $this->fulfill, + $map($this->config()), + ); } /** @@ -52,6 +57,6 @@ public function map(callable $map): self #[\Override] public function config(): Config { - return Config::new(); + return $this->config ?? Config::new(); } }