diff --git a/CHANGELOG.md b/CHANGELOG.md index 9530b0e..bf2b7d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [Unreleased] +### Changed + +- Requires `innmind/foundation:~1.9` +- Requires `innmind/http-parser:~3.0` + ### Fixed - PHP `8.4` deprecations diff --git a/composer.json b/composer.json index 70058bc..b45d0fe 100644 --- a/composer.json +++ b/composer.json @@ -16,12 +16,8 @@ }, "require": { "php": "~8.2", - "innmind/immutable": "~5.7", - "innmind/mantle": "~2.0", - "innmind/io": "~2.7", - "innmind/http-parser": "~2.1", - "innmind/cli": "^3.3", - "innmind/operating-system": "~5.0" + "innmind/foundation": "~1.9", + "innmind/http-parser": "~3.0" }, "autoload": { "psr-4": { @@ -37,8 +33,5 @@ "innmind/static-analysis": "^1.2.1", "innmind/black-box": "~6.5", "innmind/coding-standard": "~2.0" - }, - "provide": { - "innmind/mantle-sources": "1.0" } } diff --git a/src/Command/Serve.php b/src/Command/Serve.php index 831a4cc..485b75e 100644 --- a/src/Command/Serve.php +++ b/src/Command/Serve.php @@ -11,10 +11,11 @@ }; use Innmind\CLI\{ Command, + Command\Usage, Console, }; use Innmind\OperatingSystem\OperatingSystem; -use Innmind\Mantle\Forerunner; +use Innmind\Async\Scheduler; use Innmind\Http\{ ServerRequest, Response, @@ -22,6 +23,7 @@ }; use Innmind\Url\Authority\Port; use Innmind\IP\IP; +use Innmind\Immutable\Attempt; final class Serve implements Command { @@ -41,7 +43,7 @@ private function __construct( } #[\Override] - public function __invoke(Console $console): Console + public function __invoke(Console $console): Attempt { $port = $console ->options() @@ -78,32 +80,36 @@ public static function of( * @psalm-mutation-free */ #[\Override] - public function usage(): string + public function usage(): Usage { - return << + */ + private function serve(Console $console, Open $open): Attempt { - $source = Server::of( + $server = Server::of( $this->os->clock(), $open, InjectEnvironment::of(HttpEnv::of($console->variables())), $this->handle, ); - $forerunner = Forerunner::of($this->os); if ($console->options()->contains('no-output')) { - $source = $source->withOutput(Nothing::of()); + $server = $server->withOutput(Nothing::of()); } - return $forerunner($console, $source); + return Scheduler::of($this->os) + ->sink(Attempt::result($console)) + ->with($server); } } diff --git a/src/Display.php b/src/Display.php index 347e86d..95281e6 100644 --- a/src/Display.php +++ b/src/Display.php @@ -8,7 +8,10 @@ Everything, }; use Innmind\CLI\Console; -use Innmind\Immutable\Str; +use Innmind\Immutable\{ + Attempt, + Str, +}; /** * @psalm-immutable @@ -22,7 +25,10 @@ private function __construct(Output $output) $this->output = $output; } - public function __invoke(Console $console, Str $data): Console + /** + * @return Attempt + */ + public function __invoke(Console $console, Str $data): Attempt { return ($this->output)($console, $data); } diff --git a/src/Display/Everything.php b/src/Display/Everything.php index e1bebac..0e2485e 100644 --- a/src/Display/Everything.php +++ b/src/Display/Everything.php @@ -4,7 +4,10 @@ namespace Innmind\Async\HttpServer\Display; use Innmind\CLI\Console; -use Innmind\Immutable\Str; +use Innmind\Immutable\{ + Attempt, + Str, +}; /** * @psalm-immutable @@ -16,7 +19,7 @@ private function __construct() } #[\Override] - public function __invoke(Console $env, Str $data): Console + public function __invoke(Console $env, Str $data): Attempt { return $env->output($data); } diff --git a/src/Display/Nothing.php b/src/Display/Nothing.php index 53b0212..7e9b862 100644 --- a/src/Display/Nothing.php +++ b/src/Display/Nothing.php @@ -4,7 +4,10 @@ namespace Innmind\Async\HttpServer\Display; use Innmind\CLI\Console; -use Innmind\Immutable\Str; +use Innmind\Immutable\{ + Attempt, + Str, +}; /** * @psalm-immutable @@ -16,9 +19,9 @@ private function __construct() } #[\Override] - public function __invoke(Console $env, Str $data): Console + public function __invoke(Console $env, Str $data): Attempt { - return $env; + return Attempt::result($env); } /** diff --git a/src/Display/Output.php b/src/Display/Output.php index 58a3347..1c50d7b 100644 --- a/src/Display/Output.php +++ b/src/Display/Output.php @@ -4,12 +4,18 @@ namespace Innmind\Async\HttpServer\Display; use Innmind\CLI\Console; -use Innmind\Immutable\Str; +use Innmind\Immutable\{ + Attempt, + Str, +}; /** * @psalm-immutable */ interface Output { - public function __invoke(Console $env, Str $data): Console; + /** + * @return Attempt + */ + public function __invoke(Console $env, Str $data): Attempt; } diff --git a/src/Main.php b/src/Main.php index 23ba9c9..b731bed 100644 --- a/src/Main.php +++ b/src/Main.php @@ -14,11 +14,12 @@ ServerRequest, Response, }; +use Innmind\Immutable\Attempt; abstract class Main extends Cli { #[\Override] - protected function main(Environment $env, OperatingSystem $os): Environment + protected function main(Environment $env, OperatingSystem $os): Attempt { $run = Commands::of(Serve::of($os, static::handle(...))); diff --git a/src/Open.php b/src/Open.php index 73ccdfe..f71fbf3 100644 --- a/src/Open.php +++ b/src/Open.php @@ -4,8 +4,10 @@ namespace Innmind\Async\HttpServer; use Innmind\OperatingSystem\OperatingSystem; -use Innmind\IO\Sockets\Server; -use Innmind\Socket\Internet\Transport; +use Innmind\IO\Sockets\{ + Servers\Server, + Internet\Transport, +}; use Innmind\IP\{ IP, IPv4, @@ -14,6 +16,7 @@ use Innmind\Immutable\{ Sequence, Maybe, + Predicate\Instance, }; final class Open @@ -34,10 +37,9 @@ private function __construct(Sequence $addresses) */ public function __invoke(OperatingSystem $os): Maybe { - /** - * @psalm-suppress NamedArgumentNotAllowed - * @var Maybe - */ + /** @var Server|Server\Pool|null */ + $server = null; + return $this ->addresses ->map(static fn($address) => $os->ports()->open( @@ -45,14 +47,21 @@ public function __invoke(OperatingSystem $os): Maybe $address[1], $address[0], )) - ->match( - static fn($server, $rest) => Maybe::all($server, ...$rest->toList())->map( - static fn(Server $server, Server ...$servers) => Sequence::of(...$servers)->reduce( - $server, - static fn(Server|Server\Pool $pool, $server) => $pool->with($server), - ), + ->sink($server) + ->attempt( + static fn($pool, $server) => $server->map( + static fn($server) => match (true) { + \is_null($pool) => $server, + $pool instanceof Server => $pool->pool($server), + default => $pool->with($server), + }, + ), + ) + ->maybe() + ->keep( + Instance::of(Server::class)->or( + Instance::of(Server\Pool::class), ), - static fn() => Maybe::nothing(), ); } diff --git a/src/Server.php b/src/Server.php index 229bd34..eaa5d3d 100644 --- a/src/Server.php +++ b/src/Server.php @@ -5,9 +5,9 @@ use Innmind\Async\HttpServer\Display\Output; use Innmind\CLI\Console; -use Innmind\Mantle\{ - Source\Continuation, - Task, +use Innmind\Async\{ + Scope\Continuation, + Task\Discard, }; use Innmind\OperatingSystem\OperatingSystem; use Innmind\TimeContinuum\Clock; @@ -25,8 +25,9 @@ Response\StatusCode, ProtocolVersion, }; -use Innmind\IO\Sockets\Server as IOServer; +use Innmind\IO\Sockets\Servers\Server as IOServer; use Innmind\Immutable\{ + Attempt, Sequence, Maybe, Str, @@ -62,27 +63,39 @@ private function __construct( } /** - * @param Continuation $continuation - * @param Sequence $terminated + * @param Attempt $console + * @param Continuation> $continuation + * @param Sequence $results * - * @return Continuation + * @return Continuation> */ public function __invoke( - Console $console, + Attempt $console, OperatingSystem $os, Continuation $continuation, - Sequence $terminated, + Sequence $results, ): Continuation { + $failed = $console->match( + static fn() => false, + static fn() => true, + ); + + if ($failed) { + return $continuation->terminate(); + } + if (\is_null($this->servers)) { $this->servers = ($this->open)($os)->match( - static fn($servers) => $servers->watch(), + static fn($servers) => $servers, static fn() => null, ); if (!\is_null($this->servers)) { - $console = ($this->display)( - $console, - Str::of("HTTP server ready!\n"), + $console = $console->flatMap( + fn($console) => ($this->display)( + $console, + Str::of("HTTP server ready!\n"), + ), ); } } @@ -91,13 +104,18 @@ public function __invoke( return $continuation ->carryWith( $console - ->error(Str::of("Failed to open sockets\n")) - ->exit(1), + ->map(static fn($console) => $console->exit(1)) + ->flatMap(static fn($console) => $console->error(Str::of("Failed to open sockets\n"))), ) ->terminate(); } - $console = ($this->display)($console, Str::of("Pending connections...\n")); + $console = $console->flatMap( + fn($console) => ($this->display)( + $console, + Str::of("Pending connections...\n"), + ), + ); $injectEnv = $this->injectEnv; $handle = $this->handle; @@ -106,7 +124,7 @@ public function __invoke( $connections = $this ->servers ->accept() - ->map(static fn($connection) => Task::of(static function($os) use ( + ->map(static fn($connection) => static function(OperatingSystem $os) use ( $connection, $injectEnv, $handle, @@ -140,10 +158,13 @@ public function __invoke( null, Content::ofString('Request doesn\'t respect HTTP protocol'), ))) - ->flatMap(static fn($response) => $connection->send($encode($response))) + ->flatMap( + static fn($response) => $connection + ->sink($encode($response)) + ->maybe(), + ) ->flatMap( static fn() => $connection - ->unwrap() ->close() ->maybe(), ) @@ -151,17 +172,26 @@ public function __invoke( static fn() => null, // response sent static fn() => null, // failed to send response or close connection ); - })); - if ($connections instanceof Maybe) { - $connections = $connections->toSequence(); + return Discard::result; + }); + + if ($connections instanceof Attempt) { + $connections = $connections + ->maybe() + ->toSequence(); } - $console = ($this->display)($console, Str::of("New connections: {$connections->size()}\n")); + $console = $console->flatMap( + fn($console) => ($this->display)( + $console, + Str::of("New connections: {$connections->size()}\n"), + ), + ); return $continuation ->carryWith($console) - ->launch($connections->memoize()); + ->schedule($connections->memoize()); } /** diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php index 53a7f23..757ec7d 100644 --- a/tests/FunctionalTest.php +++ b/tests/FunctionalTest.php @@ -32,7 +32,8 @@ public function setUp(): void ->execute( Command::foreground('php fixtures/server.php') ->withEnvironment('PATH', \getenv('PATH')), - ); + ) + ->unwrap(); } public function tearDown(): void @@ -41,7 +42,7 @@ public function tearDown(): void fn($pid) => $this->os->control()->processes()->kill( $pid, Signal::kill, - ), + )->unwrap(), static fn() => null, ); } @@ -51,8 +52,8 @@ public function testServerRespond() $found = $this ->server ->output() - ->chunks() - ->find(static fn($pair) => $pair[0]->startsWith('HTTP server ready!')); + ->map(static fn($chunk) => $chunk->data()) + ->find(static fn($chunk) => $chunk->startsWith('HTTP server ready!')); $this->assertTrue($found->match( static fn() => true, static fn() => false,