diff --git a/src/Connection/Internal/Http2ConnectionProcessor.php b/src/Connection/Internal/Http2ConnectionProcessor.php index 3d7f7751..c0349542 100644 --- a/src/Connection/Internal/Http2ConnectionProcessor.php +++ b/src/Connection/Internal/Http2ConnectionProcessor.php @@ -615,6 +615,10 @@ public function handlePushPromise(int $streamId, int $pushId, array $pseudo, arr $request->setInactivityTimeout($parentStream->request->getInactivityTimeout()); $request->setTransferTimeout($parentStream->request->getTransferTimeout()); + foreach ($parentStream->request->getEventListeners() as $eventListener) { + $request->addEventListener($eventListener); + } + $deferredCancellation = new DeferredCancellation(); $cancellation = new CompositeCancellation( @@ -648,6 +652,17 @@ static function () { events()->requestBodyStart($request, $stream->stream); events()->requestBodyEnd($request, $stream->stream); + $stream->pendingResponse->getFuture() + ->map(function (Response $response) use ($request) { + $response->getTrailers()->map(fn () => events()->requestEnd($request, $response))->ignore(); + $response->getTrailers()->catch(fn (\Throwable $e) => events()->requestFailed( + $request, + $e instanceof HttpException ? $e : new HttpException('Unexpected exception: ' . $e->getMessage(), previous: $e) + ))->ignore(); + }) + ->catch(fn (\Throwable $e) => events()->requestFailed($request, $e instanceof HttpException ? $e : new HttpException('Unexpected exception: ' . $e->getMessage(), previous: $e))) + ->ignore(); + $stream->dependency = $streamId; $this->streams[$pushId] = $stream; diff --git a/test/ClientHttpBinIntegrationTest.php b/test/ClientHttpBinIntegrationTest.php index 1c606742..72444ae0 100644 --- a/test/ClientHttpBinIntegrationTest.php +++ b/test/ClientHttpBinIntegrationTest.php @@ -217,13 +217,22 @@ public function testHeaderCase(): void public function testHttp2Push(): void { + $eventListener = $this->createMock(EventListener::class); + $eventListener->expects($this->exactly(2))->method('requestStart'); + $eventListener->expects($this->exactly(2))->method('requestEnd'); + $eventListener->expects($this->exactly(1))->method('push'); + + $this->givenEventListener($eventListener); + $request = new Request('https://http2-server-push-demo.keksi.io/'); $request->setPushHandler(static function (Request $request, Future $response) use ( &$path, &$contentType, &$future, + &$pushedRequest ): void { $future = $response; + $pushedRequest = $request; $path = $request->getUri()->getPath(); $contentType = $response->await()->getHeader('content-type'); @@ -231,7 +240,9 @@ public function testHttp2Push(): void $this->executeRequest($request); - $future->await(); + /** @var Response $pushedResponse */ + $pushedResponse = $future->await(); + $pushedResponse->getTrailers()->await(); self::assertSame('/image.jpg', $path); self::assertSame('image/jpeg', $contentType); @@ -818,9 +829,9 @@ private function createRequest(): Request return new Request('http://' . $this->socket->getAddress()); } - private function givenApplicationInterceptor(ApplicationInterceptor $interceptor): void + private function givenEventListener(EventListener $eventListener): void { - $this->builder = $this->builder->intercept($interceptor); + $this->builder = $this->builder->listen($eventListener); $this->client = $this->builder->build(); }