diff --git a/README.md b/README.md
index 40bab8016958f90876a45dce68fac4454d7d1314..69499896270150ef8293127b56b7b81624fdb2a6 100644
--- a/README.md
+++ b/README.md
@@ -94,9 +94,9 @@ advantage of the asynchronous environment.
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 readonly class Homepage implements HttpResponderInterface
 {
-    public function respond(Request $request, Response $response): TwigTemplate
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): TwigTemplate
     {
-        return new TwigTemplate('website/homepage.twig');
+        return new TwigTemplate($request, $response, 'website/homepage.twig');
     }
 }
 ```
diff --git a/docs/pages/docs/changelog/index.md b/docs/pages/docs/changelog/index.md
index 69c6deafb7f5c243cd6297f599f89748cf512a74..25423c3aec061340b5622ac96d14c590d5c7fd69 100644
--- a/docs/pages/docs/changelog/index.md
+++ b/docs/pages/docs/changelog/index.md
@@ -81,5 +81,5 @@ title: Changelog
 ## v0.10.0
 
 - Added {{docs/features/security/oauth2/index}} support.
-- Added {{docs/features/http/psr-http-messages}} wrapper.
+- Added PSR Http Messages wrapper.
 - Added `EntityManagerWeakReference` to {{docs/features/database/doctrine/index}} integration.
diff --git a/docs/pages/docs/features/configuration/index.md b/docs/pages/docs/features/configuration/index.md
index f76d7da90f138427b80f1d0a6d1b27f75c60b9c5..54dbbd2a025fe77ec10c52a053af6b8dcbbbae1f 100644
--- a/docs/pages/docs/features/configuration/index.md
+++ b/docs/pages/docs/features/configuration/index.md
@@ -283,8 +283,8 @@ use Distantmagic\Resonance\HttpResponder;
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -310,12 +310,15 @@ final readonly class Manifest extends HttpResponder
         ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
     }
 
-    public function respond(Request $request, Response $response): ?HttpResponderInterface
+    public function respond(
+        ServerRequestInterface $request, 
+        ResponseInterface $response,
+    ): ResponseInterface
     {
-        $response->header('content-type', ContentType::ApplicationJson->value);
-        $response->end($this->manifest);
-
-        return null;
+        return $response
+            ->withHeader('content-type', ContentType::ApplicationJson->value)
+            ->withBody($this->createStream($this->manifest))
+        ;
     }
 }
 ```
diff --git a/docs/pages/docs/features/database/doctrine/entities.md b/docs/pages/docs/features/database/doctrine/entities.md
index d927fb5e8ef313fa0135730adabd097473b220ad..ee6c758c1141d292629018912912b8ecd78ca697 100644
--- a/docs/pages/docs/features/database/doctrine/entities.md
+++ b/docs/pages/docs/features/database/doctrine/entities.md
@@ -55,6 +55,8 @@ use Distantmagic\Resonance\HttpResponder\HttpController;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\TwigTemplate;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -64,7 +66,9 @@ use Distantmagic\Resonance\TwigTemplate;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class BlogPostShow extends HttpController
 {
-    public function handle(
+    public function createResponse(
+        ServerRequestInterface $request,
+        ResponseInterface $response,
         #[DoctrineEntityRouteParameter(
             from: 'blog_post_slug', 
             intent: CrudAction::Read,
@@ -72,9 +76,14 @@ final readonly class BlogPostShow extends HttpController
         )]
         BlogPost $blogPost,
     ): HttpInterceptableInterface {
-        return new TwigTemplate('turbo/website/blog_post.twig', [
-            'blog_post' => $blogPost,
-        ]);
+        return new TwigTemplate(
+            $request, 
+            $response, 
+            'turbo/website/blog_post.twig', 
+            [
+                'blog_post' => $blogPost,
+            ],
+        );
     }
 }
 ```
diff --git a/docs/pages/docs/features/database/doctrine/entity-managers.md b/docs/pages/docs/features/database/doctrine/entity-managers.md
index 4e2ea6ce5de740c85ea494b8ef8883e5a1c81ed2..f25c5c154f5a88e375870f22ef3b7ed69e5846cb 100644
--- a/docs/pages/docs/features/database/doctrine/entity-managers.md
+++ b/docs/pages/docs/features/database/doctrine/entity-managers.md
@@ -90,8 +90,8 @@ use Distantmagic\Resonance\HttpResponder;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\TwigTemplate;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -105,15 +105,23 @@ final readonly class Blog extends HttpResponder
         private DoctrineEntityManagerRepository $doctrineEntityManagerRepository,
     ) {}
 
-    public function respond(Request $request, Response $response): HttpInterceptableInterface
+    public function respond(
+        ServerRequestInterface $request, 
+        ResponseInterface $response,
+    ): HttpInterceptableInterface
     {
         $entityManager = $this->doctrineEntityManagerRepository->getEntityManager($request);
 
         $blogPostsRepository = $entityManager->getRepository(BlogPost::class);
 
-        return new TwigTemplate('turbo/website/blog.twig', [
-            'blog_posts' => $blogPostsRepository->findAll(),
-        ]);
+        return new TwigTemplate(
+            $request,
+            $response,
+            'turbo/website/blog.twig', 
+            [
+                'blog_posts' => $blogPostsRepository->findAll(),
+            ]
+        );
     }
 }
 ```
@@ -147,6 +155,8 @@ use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\TwigTemplate;
 use Doctrine\ORM\EntityManager;
 use Doctrine\ORM\EntityRepository;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -156,15 +166,22 @@ use Doctrine\ORM\EntityRepository;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class Blog extends HttpController
 {
-    public function handle(
+    public function createResponse(
+        ServerRequestInterface $request,
+        ResponseInterface $response,
         #[DoctrineEntityManager]
         EntityManager $entityManager,
         #[DoctrineEntityRepository(BlogPost::class)]
         EntityRepository $blogPosts,
     ): HttpInterceptableInterface {
-        return new TwigTemplate('website/blog.twig', [
-            'blog_posts' => $blogPosts->findAll(),
-        ]);
+        return new TwigTemplate(
+            $request,
+            $response,
+            'website/blog.twig', 
+            [
+                'blog_posts' => $blogPosts->findAll(),
+            ]
+        );
     }
 }
 ```
diff --git a/docs/pages/docs/features/graphql/developing-schema.md b/docs/pages/docs/features/graphql/developing-schema.md
index 72c7faf8c23226643dae447a0554414e128e8f33..33c5efdfd293359d172d13bad696c24667e54fab 100644
--- a/docs/pages/docs/features/graphql/developing-schema.md
+++ b/docs/pages/docs/features/graphql/developing-schema.md
@@ -27,6 +27,7 @@ GraphQL handler.
 
 namespace App\HttpResponder;
 
+use App\HttpRouteSymbol;
 use Distantmagic\Resonance\Attribute\RespondsToHttp;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\HttpResponder;
@@ -34,9 +35,8 @@ use Distantmagic\Resonance\HttpResponder\GraphQL as ResonanceGraphQL;
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
-use App\HttpRouteSymbol;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::POST,
@@ -48,7 +48,7 @@ final readonly class GraphQL extends HttpResponder
 {
     public function __construct(private ResonanceGraphQL $graphql) {}
 
-    public function respond(Request $request, Response $response): HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface
     {
         return $this->graphql;
     }
diff --git a/docs/pages/docs/features/http/controllers.md b/docs/pages/docs/features/http/controllers.md
index 34797557119da4b1d9cfe7e4d3c8697fa69b0b28..e1d0d29609b28fb77ed4abb94cc90a3abba40e74 100644
--- a/docs/pages/docs/features/http/controllers.md
+++ b/docs/pages/docs/features/http/controllers.md
@@ -22,7 +22,8 @@ Controllers aim to automate these repetitive tasks as much as possible.
 ## Writing Controllers
 
 Unlike {{docs/features/http/responders}}, Controllers do not use the `respond` 
-method. Instead, they rely on the `handle` method to manage incoming requests. 
+method. Instead, they rely on the `createResponse` method to manage incoming 
+requests. 
 
 The `respond` method is used internally for handling tasks like parameter 
 binding, so you should not override it.
@@ -52,8 +53,8 @@ Using the `RouteParameter` might require to create a Crud Gate. See more at
 the {{docs/features/security/authorization/index}} page.
 :::
 
-Remember that the framework resolves parameters assigned to the `handle` method 
-on runtime during the request lifecycle.
+Remember that the framework resolves parameters assigned to the 
+`createResponse` method on runtime during the request lifecycle.
 
 The above is contrary to the constructor arguments, which the framework 
 resolves during the application bootstrap phase thanks to the 
@@ -78,7 +79,7 @@ use Distantmagic\Resonance\HttpResponder\HttpController;
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -88,13 +89,13 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class BlogPostShow extends HttpController
 {
-    public function handle(
+    public function createResponse(
         #[RouteParameter(
             from: 'blog_post_slug', 
             intent: CrudAction::Read,
         )]
         BlogPost $blogPost,
-        Response $response,
+        ResponseInterface $response,
     ): HttpResponderInterface {
         // ...
     }
@@ -142,8 +143,8 @@ final readonly class BlogPostBinder implements HttpRouteParameterBinderInterface
 ### Providing the Authenticated User (Session)
 
 If you need to fetch the authenticated user in your controller, you can add 
-a parameter with the `#[SessionAuthenticated]` attribute to the `handle` 
-method.
+a parameter with the `#[SessionAuthenticated]` attribute to the 
+`createResponse` method.
 
 The controller fetches an authenticated user through 
 {{docs/features/http/sessions}}.
@@ -162,7 +163,6 @@ use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\UserInterface;
-use Swoole\Http\Response;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -172,7 +172,7 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class MyController extends HttpController
 {
-    public function handle(
+    public function createResponse(
         // If you make this parameter required, then the framework will
         // return 403 page when user is unauthenticated.
         #[SessionAuthenticated]
@@ -201,8 +201,8 @@ use Distantmagic\Resonance\HttpControllerParameterResolution;
 use Distantmagic\Resonance\HttpControllerParameterResolutionStatus;
 use Distantmagic\Resonance\HttpControllerParameterResolver;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 /**
  * @template-extends HttpControllerParameterResolver<MyAttribute>
@@ -212,8 +212,8 @@ use Swoole\Http\Response;
 readonly class RouteParameterResolver extends HttpControllerParameterResolver
 {
     public function resolve(
-        Request $request,
-        Response $response,
+        ServerRequestInterface $request,
+        ResponseInterface $response,
         HttpControllerParameter $httpControllerParameter,
         Attribute $attribute,
     ): HttpControllerParameterResolution {
@@ -248,7 +248,7 @@ readonly class MyHttpController extends HttpController
         parent::__construct($httpControllerDependencies);
     }
 
-    public function handle()
+    public function createResponse()
     {
         // ...
     }
diff --git a/docs/pages/docs/features/http/interceptors.md b/docs/pages/docs/features/http/interceptors.md
index b85a39b4ee0696779de08c8b3db89824670eec3e..3179659fcabd443b9b22477a2bcb3de18c12f769 100644
--- a/docs/pages/docs/features/http/interceptors.md
+++ b/docs/pages/docs/features/http/interceptors.md
@@ -105,8 +105,8 @@ use Distantmagic\Resonance\HttpInterceptableInterface;
 use Distantmagic\Resonance\HttpInterceptor;
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 /**
  * @template-extends HttpInterceptor<Hello>
@@ -116,14 +116,14 @@ use Swoole\Http\Response;
 readonly class HelloInterceptor extends HttpInterceptor
 {
     public function intercept(
-        Request $request,
-        Response $response,
+        ServerRequestInterface $request,
+        ResponseInterface $response,
         object $intercepted,
     ): HttpInterceptableInterface|HttpResponderInterface {
-        $response->header('content-type', 'text/plain');
-        $response->end('Hello, '.$intercepted->message.'!');
-
-        return null;
+        return $response
+            ->withHeader('content-type', 'text/plain')
+            ->withBody($this->createStream('Hello, '.$intercepted->message.'!'))
+        ;
     }
 }
 ```
@@ -143,8 +143,8 @@ use Distantmagic\Resonance\HttpInterceptableInterface;
 use Distantmagic\Resonance\HttpResponder;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -154,7 +154,7 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class HelloResponder extends HttpResponder
 {
-    public function respond(Request $request, Response $response): HttpInterceptableInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
     {
         return new Hello('World');
     }
diff --git a/docs/pages/docs/features/http/middleware.md b/docs/pages/docs/features/http/middleware.md
index 8fbbe351ff7164b81e0fd2a7f13dde27410d9dd8..b2845dedf5eae9b659ccedf92da26c4f9f22fa2e 100644
--- a/docs/pages/docs/features/http/middleware.md
+++ b/docs/pages/docs/features/http/middleware.md
@@ -92,13 +92,15 @@ use Distantmagic\Resonance\Attribute\ValidatesCSRFToken;
 #[ValidatesCSRFToken]
 final readonly class BlogPostDestroy extends HttpController
 {
-    public function handle(
+    public function createResponse(
+        ServerRequestInterface $request,
+        ResponseInterface $response,
         #[RouteParameter(from: 'blog_post_slug', intent: CrudAction::Delete)]
         BlogPost $blogPost,
     ): HttpInterceptableInterface {
         // ...
 
-        return new InternalRedirect(HttpRouteSymbol::Blog);
+        return new InternalRedirect($request, $response, HttpRouteSymbol::Blog);
     }
 }
 
@@ -161,8 +163,8 @@ use Distantmagic\Resonance\HttpInterceptableInterface;
 use Distantmagic\Resonance\HttpMiddleware;
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 /**
  * @template-extends HttpMiddleware<MyAttribute>
@@ -175,8 +177,8 @@ use Swoole\Http\Response;
 readonly class CanMiddleware extends HttpMiddleware
 {
     public function preprocess(
-        Request $request,
-        Response $response,
+        ServerRequestInterface $request,
+        ResponseInterface $response,
         Attribute $attribute,
         HttpInterceptableInterface|HttpResponderInterface $next,
     ): HttpInterceptableInterface|HttpResponderInterface {
diff --git a/docs/pages/docs/features/http/psr-http-messages.md b/docs/pages/docs/features/http/psr-http-messages.md
deleted file mode 100644
index ed4d9f79b6c45ceb54eb95bc7ef03fee6543aee0..0000000000000000000000000000000000000000
--- a/docs/pages/docs/features/http/psr-http-messages.md
+++ /dev/null
@@ -1,62 +0,0 @@
----
-collections: 
-    - documents
-layout: dm:document
-parent: docs/features/http/index
-title: PSR HTTP Messages
-description: >
-    Learn how to convert Swoole server requests to PSR server requests.
----
-
-# PSR HTTP Messages
-
-If you need to convert Swoole's HTTP Request object to it's 
-[PSR counterpart](https://www.php-fig.org/psr/psr-7/) you can use the 
-converters.
-
-# Usage
-
-## Swoole Request -> PSR Server Request
-
-You should only convert requests if you need to use some third-party library 
-that relies on them. Primarily because PSR requests do not provide any 
-additional features, it's just for standardization. Conversion between request
-formats hinders the performance.
-
-`PsrServerRequestConverter` can/should also be used as a singleton. 
-
-```php
-/**
- * @var Distantmagic\Resonance\PsrServerRequestConverter $psrServerRequestRepository 
- * @var Swoole\Http\Request $request 
- * @var Psr\Http\Message\ServerRequestInterface $psrRequest
- */
-$psrRequest = $psrServerRequestRepository->convertToServerRequest($request);
-```
-
-## PSR Response -> Swoole Response
-
-If you want to respond with PSR response, you need to wrap it in 
-`PsrResponder`:
-
-```php
-use Distantmagic\Resonance\HttpResponder;
-use Distantmagic\Resonance\HttpResponderInterface;
-use Distantmagic\Resonance\HttpResponder\PsrResponder;
-use Psr\Http\Message\ResponseInterface;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
-
-readonly class MyResponder extends HttpResponder
-{
-    public function respond(Request $request, Response $response): HttpResponderInterface
-    {
-        // (...) obtain psr response somehow
-
-        /**
-         * @var ResponseInterface $psrResponse
-         */
-        return new PsrResponder($psrResponse);
-    }
-}
-```
diff --git a/docs/pages/docs/features/http/responders.md b/docs/pages/docs/features/http/responders.md
index d82305c90315ca19cc1cc88a9404de1a6862e88d..81c6cda206656365e322ddc1c7a23472ef665e6d 100644
--- a/docs/pages/docs/features/http/responders.md
+++ b/docs/pages/docs/features/http/responders.md
@@ -58,8 +58,8 @@ use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -69,11 +69,9 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class Homepage implements HttpResponderInterface
 {
-    public function respond(Request $request, Response $response): ?HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
     {
-        $response->end('Hello, world!');
-
-        return null;
+        return $response->withBody($this->createStream('Hello, world!'));
     }
 }
 ```
@@ -90,14 +88,14 @@ For example:
 
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\HttpResponder\Redirect;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 class MyResponder implements HttpResponderInterface
 {
     // (...)
 
-    public function respond(Request $request, Response $response): HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface
     {
         return new Redirect('/blog');
     }
@@ -110,18 +108,16 @@ You can even use anonymous classes:
 <?php
 
 use Distantmagic\Resonance\HttpResponderInterface;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 class MyResponder implements HttpResponderInterface
 {
     public function respond(Request $request, Response $response): ?HttpResponderInterface
     {
         return new class implements HttpResponderInterface {
-            public function respond (Request $request, Response $response): null {
-                $response->end('Hello!');
-
-                return null;
+            public function respond (ServerRequestInterface $request, ResponseInterface $response): ResponseInterface {
+                return $response->withBody($this->createStream('Hello!'));
             }
         };
     }
diff --git a/docs/pages/docs/features/http/serving-assets.md b/docs/pages/docs/features/http/serving-assets.md
index 00b457117ef70e55da95c69538f69daee78b4dc1..b62649dd671121a1020b48ae9466aa14bbb3d345 100644
--- a/docs/pages/docs/features/http/serving-assets.md
+++ b/docs/pages/docs/features/http/serving-assets.md
@@ -47,8 +47,8 @@ use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\HttpRouteMatchRegistry;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -66,7 +66,7 @@ final readonly class Asset extends HttpResponder
         private HttpRouteMatchRegistry $routeMatchRegistry,
     ) {}
 
-    public function respond(Request $request, Response $response): ?HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface|ResponseInterface
     {
         return $this->assetFileRegistry->sendAsset(
             $response, 
diff --git a/docs/pages/docs/features/http/sessions.md b/docs/pages/docs/features/http/sessions.md
index 7c42d17ffd523d806b891c28a98ce6c2f5b28742..9fe4a773aec89a902f3b76b2689c212457a007a7 100644
--- a/docs/pages/docs/features/http/sessions.md
+++ b/docs/pages/docs/features/http/sessions.md
@@ -77,10 +77,10 @@ The typical usage follows the 'start -> modify -> persist' pattern:
  * `$sessionManager->start()` also sets the session cookie in the response.
  * 
  * @var Resonance\Session $session
- * @var Swoole\Http\Request $request
+ * @var Psr\Http\Message\ServerRequestInterface $request
  * @var Swoole\Http\Response $response
  */
-$session = $sessionManager->start($request, $response);
+$session = $sessionManager->start($request);
 
 /**
  * Anything that is serializable by igbinary can be used as a value.
diff --git a/docs/pages/docs/features/openapi/exposing-schema/index.md b/docs/pages/docs/features/openapi/exposing-schema/index.md
index fb121c0064a07476b0b355588a58fea74ab7484d..11b933b9169e3b4638c074b1515815c97dc1b9aa 100644
--- a/docs/pages/docs/features/openapi/exposing-schema/index.md
+++ b/docs/pages/docs/features/openapi/exposing-schema/index.md
@@ -34,8 +34,8 @@ use Distantmagic\Resonance\OpenAPISchemaBuilder;
 use Distantmagic\Resonance\OpenAPISchemaSymbol;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -51,7 +51,7 @@ readonly class OpenAPISchema implements HttpResponderInterface
         $this->schema = $openAPISchemaBuilder->toJsonResponse(OpenAPISchemaSymbol::All);
     }
 
-    public function respond(Request $request, Response $response): HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface
     {
         return $this->schema;
     }
diff --git a/docs/pages/docs/features/security/authentication/index.md b/docs/pages/docs/features/security/authentication/index.md
index 72a9eb23a90c8f324d296fb9ba7f7ecd208d5069..a8a0e053137682a3a0028ec6cde3a78f05ca2a79 100644
--- a/docs/pages/docs/features/security/authentication/index.md
+++ b/docs/pages/docs/features/security/authentication/index.md
@@ -56,7 +56,7 @@ final readonly class LoginValidation extends HttpController
         parent::__construct($controllerDependencies);
     }
 
-    public function handle(Request $request, Response $response): HttpInterceptableInterface {
+    public function createResponse(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface {
         $user = /* obtain user somehow */;
 
         $this->sessionAuthentication->setAuthenticatedUser(
@@ -65,7 +65,7 @@ final readonly class LoginValidation extends HttpController
             $user,
         );
 
-        return new InternalRedirect(HttpRouteSymbol::Homepage);
+        return new InternalRedirect($request, $response, HttpRouteSymbol::Homepage);
     }
 }
 ```
@@ -112,7 +112,7 @@ final readonly class MyController extends HttpController
     /**
      * @param ?UserInterface $user null if not authenticated
      */
-    public function handle(
+    public function createResponse(
         Request $request,
         #[SessionAuthenticated]
         ?UserInterface $user,
@@ -133,13 +133,13 @@ use Distantmagic\Resonance\Attribute\ProvidesAuthenticatedUser;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\AuthenticatedUserStoreInterface;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[ProvidesAuthenticatedUser(1200)]
 #[Singleton(collection: SingletonCollection::AuthenticatedUserStore)]
 readonly class MyAuthentication implements AuthenticatedUserStoreInterface
 {
-    public function getAuthenticatedUser(Request $request): ?AuthenticatedUser
+    public function getAuthenticatedUser(ServerRequestInterface $request): ?AuthenticatedUser
     {
         // ...
     }
@@ -158,7 +158,7 @@ use Distantmagic\Resonance\AuthenticatedUserStoreInterface;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\UserInterface;
 use League\OAuth2\Client\Provider\GenericProvider;
-use Swoole\Http\Request;
+use Psr\Http\Message\ServerRequestInterface;
 
 use function Swoole\Coroutine\Http\get;
 
@@ -166,7 +166,7 @@ use function Swoole\Coroutine\Http\get;
 #[Singleton(collection: SingletonCollection::AuthenticatedUserStore)]
 readonly class MyAuthentication implements AuthenticatedUserStoreInterface
 {
-    public function getAuthenticatedUser(Request $request): ?AuthenticatedUser
+    public function getAuthenticatedUser(ServerRequestInterface $request): ?AuthenticatedUser
     {
         $userData = get('https://your-server.example.com/user', [], [
             'Authorization' => sprintf('Bearer %s', $request->cookie['access_token']),
diff --git a/docs/pages/docs/features/security/authorization/index.md b/docs/pages/docs/features/security/authorization/index.md
index 38ee0fbc0b076560e10bdea55aa6a304471e553e..09fab519ef40acbbf411fa671744d920cd0cb34c 100644
--- a/docs/pages/docs/features/security/authorization/index.md
+++ b/docs/pages/docs/features/security/authorization/index.md
@@ -105,14 +105,14 @@ You can use `Gatekeeper` in your code to check permissions. For example:
 use App\BlogPostInterface;
 use Distantmagic\Resonance\CrudAction;
 use Distantmagic\Resonance\Gatekeeper;
-use Swoole\Http\Request;
+use Psr\Http\Message\ServerRequestInterface;
 
 readonly class MyClass
 {
     public function __construct(private Gatekeeper $gatekeeper) {}
 
     public function showBlogPost(
-        Request $request,
+        ServerRequestInterface $request,
         BlogPostInterface $blogPost,
     ): string
     {
diff --git a/docs/pages/docs/features/security/content-security-policy/index.md b/docs/pages/docs/features/security/content-security-policy/index.md
index a9729dbfeac0a08b01aa79b537577f289757ff49..85e775a628ea8d35841d9e6e3aa64c4dc38b8d2b 100644
--- a/docs/pages/docs/features/security/content-security-policy/index.md
+++ b/docs/pages/docs/features/security/content-security-policy/index.md
@@ -59,7 +59,7 @@ To use nonces manually, you need to use the CSP Nonce Manager:
 ```php
 /**
  * @var \Distantmagic\Resonance\CSPNonceManager $cspNonceManager
- * @var \Swoole\Http\Request $request
+ * @var \Psr\Http\Message\ServerRequestInterface $request
  */
 $cspNonceManager->getRequestNonce($request);
 ```
diff --git a/docs/pages/docs/features/security/csrf-protection/index.md b/docs/pages/docs/features/security/csrf-protection/index.md
index 393de1773ea0d7c0243d32dab66fc311ac453dec..83271a07903fd7a2a36c2b5b2124fe3a1be21bc4 100644
--- a/docs/pages/docs/features/security/csrf-protection/index.md
+++ b/docs/pages/docs/features/security/csrf-protection/index.md
@@ -103,14 +103,14 @@ response and the `respond` method will not be called.
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Attribute\ValidatesCSRFToken;
 use Distantmagic\Resonance\HttpResponderInterface;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[Singleton]
 #[ValidatesCSRFToken]
 final readonly class MyResponder implements HttpResponderInterface
 {
-    public function respond(Request $request, Response $response): void 
+    public function respond(ServerRequestInterface $request, ResponseInterfaced $response): void 
     {
         // CSRF token is valid...
     }
@@ -124,24 +124,24 @@ final readonly class MyResponder implements HttpResponderInterface
 
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\CSRFManager;
-use Distantmagic\Resonance\HttpResponderInterface;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Distantmagic\Resonance\HttpResponder;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[Singleton]
-final readonly class MyResponder implements HttpResponderInterface
+final readonly class MyResponder extends HttpResponder
 {
     public function __construct(private CSRFManager $csrfManager) 
     {
     }
 
-    public function respond(Request $request, Response $response): void 
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
     {
-        if (!$this->csrfManager->checkToken($request, $request->post)) {
-            $response->status(400);
-            $response->end('Bad Request: CSRF Token is invalid.');
-
-            return;
+        if (!$this->csrfManager->checkToken($request, $request->getParsedBody())) {
+            return $response
+                ->withStatus(400)
+                ->withBody($this->createStream('Bad Request: CSRF Token is invalid.'))
+            ;
         }
 
         // CSRF token is valid...
diff --git a/docs/pages/docs/features/security/oauth2/authorization-code-grant/index.md b/docs/pages/docs/features/security/oauth2/authorization-code-grant/index.md
index 8d98bd94aa4201b06e7880e342e942ef6df35b1f..4a22204ca39c1df1a3e84b93e72b861ca9fb720c 100644
--- a/docs/pages/docs/features/security/oauth2/authorization-code-grant/index.md
+++ b/docs/pages/docs/features/security/oauth2/authorization-code-grant/index.md
@@ -85,8 +85,8 @@ use Distantmagic\Resonance\HttpResponder\OAuth2\Authorization;
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::POST,
@@ -98,7 +98,7 @@ final readonly class OAuth2AuthorizationServer extends HttpResponder
 {
     public function __construct(private Authorization $authorizationServer) {}
 
-    public function respond(Request $request, Response $response): HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface
     {
         return $this->authorizationServer;
     }
diff --git a/docs/pages/docs/features/security/oauth2/configuration/index.md b/docs/pages/docs/features/security/oauth2/configuration/index.md
index 52f5f61544c7992b1081b991504442dc8dc4d443..b9d70000f7c638c3555a35445fa2f8a0d523b92a 100644
--- a/docs/pages/docs/features/security/oauth2/configuration/index.md
+++ b/docs/pages/docs/features/security/oauth2/configuration/index.md
@@ -51,8 +51,8 @@ use Distantmagic\Resonance\HttpResponder\OAuth2\AccessToken;
 use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::POST,
@@ -64,7 +64,7 @@ final readonly class OAuth2AccessToken extends HttpResponder
 {
     public function __construct(private AccessToken $accessTokenResponder) {}
 
-    public function respond(Request $request, Response $response): HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface
     {
         return $this->accessTokenResponder;
     }
@@ -88,8 +88,8 @@ use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\PsrServerRequestConverter;
 use League\OAuth2\Server\AuthorizationServer as LeagueAuthorizationServer;
 use Nyholm\Psr7\Factory\Psr17Factory;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[Singleton]
 readonly class MyOAuth2Server extends HttpResponder
@@ -100,7 +100,7 @@ readonly class MyOAuth2Server extends HttpResponder
         private Psr17Factory $psr17Factory,
     ) {}
 
-    public function respond(Request $request, Response $response): HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface
     {
         /**
          * Convert Swoole http request to PSR Server request object
diff --git a/docs/pages/docs/features/templating/php-templates/index.md b/docs/pages/docs/features/templating/php-templates/index.md
index 6ea8cc669e0df95c63d4ddee9c45cd3dafa88db3..7762a3b684a72a72a63c91de2197c360ed1a995b 100644
--- a/docs/pages/docs/features/templating/php-templates/index.md
+++ b/docs/pages/docs/features/templating/php-templates/index.md
@@ -39,11 +39,11 @@ Template file:
 ```php
 <?php
 
-use Distantmagic\Resonance\HttpResponderInterface;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Distantmagic\Resonance\HttpResponder;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
-readonly class MyBlogPostTemplate implements HttpResponderInterface
+readonly class MyBlogPostTemplate extends HttpResponder
 {
     public function __construct(
         private string $title, 
@@ -52,9 +52,9 @@ readonly class MyBlogPostTemplate implements HttpResponderInterface
     {
     }
 
-    public function respond(Request $request, Response $response): null
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
     {
-        $response->end(<<<HTML
+        return $response->with($this->createStream(<<<HTML
         <html>
             <head></head>
             <body>
@@ -62,9 +62,7 @@ readonly class MyBlogPostTemplate implements HttpResponderInterface
                 <p>{$this->content}</p>
             </body>
         </html>
-        HTML);
-
-        return null;
+        HTML));
     }
 }
 ```
@@ -75,12 +73,12 @@ Responder:
 <?php
 
 use Distantmagic\Resonance\HttpResponderInterface;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 readonly class MyResponder implements HttpResponderInterface
 {
-    public function respond(Request $request, Response $response): HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface
     {
         return new MyTemplate('title', 'content');
     }
@@ -103,17 +101,17 @@ kind of performance.
 ```php
 <?php
 
-use Distantmagic\Resonance\HttpResponderInterface;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Distantmagic\Resonance\HttpResponder;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
-abstract readonly class MyBaseTemplate implements HttpResponderInterface
+abstract readonly class MyBaseTemplate extends HttpResponder
 {
     abstract protected function renderBodyContent(Request $request, Response $response): string;
 
-    public function respond(Request $request, Response $response): null
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
     {
-        $response->end(<<<HTML
+        return $response->withBody($this->createStream(<<<HTML
         <html>
             <head></head>
             <body>
@@ -121,9 +119,7 @@ abstract readonly class MyBaseTemplate implements HttpResponderInterface
                 {$this->renderBodyContent($request, $response)}
             </body>
         </html>
-        HTML);
-
-        return null;
+        HTML));
     }
 }
 ```
@@ -133,12 +129,12 @@ Then in other pages:
 ```php
 <?php
 
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 readonly class MyBlogPost extends MyBaseTemplate
 {
-    protected function renderBodyContent(Request $request, Response $response): string
+    protected function renderBodyContent(ServerRequestInterface $request, ResponseInterface $response): string
     {
         return 'Hello!';
     }
diff --git a/docs/pages/docs/features/templating/twig/rendering-templates.md b/docs/pages/docs/features/templating/twig/rendering-templates.md
index 93d262f301337a6c8d75224f8e5288756e94c639..ab42b7fcbbb4c763b83fdd39b46d1bd518054a4a 100644
--- a/docs/pages/docs/features/templating/twig/rendering-templates.md
+++ b/docs/pages/docs/features/templating/twig/rendering-templates.md
@@ -27,8 +27,8 @@ use Distantmagic\Resonance\HttpInterceptableInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\TwigTemplate;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -38,9 +38,9 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class Twig extends HttpResponder
 {
-    public function respond(Request $request, Response $response): HttpInterceptableInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
     {
-        return new TwigTemplate('test.twig');
+        return new TwigTemplate($request, $response, 'test.twig');
     }
 }
 ```
diff --git a/docs/pages/docs/features/translations/index.md b/docs/pages/docs/features/translations/index.md
index 8040af1aa248fdd91b8ebdf826017d015e259283..1e9dbe9cc99cfdc8f5f0bbc65ebaa11b12e44dbd 100644
--- a/docs/pages/docs/features/translations/index.md
+++ b/docs/pages/docs/features/translations/index.md
@@ -50,7 +50,7 @@ header).
 
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\TranslatorBridge;
-use Swoole\Http\Request;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[Singleton]
 readonly class MyClass
@@ -59,7 +59,7 @@ readonly class MyClass
     {
     }
 
-    public function doSomething(Request $request): string
+    public function doSomething(ServerRequestInterface $request): string
     {
         return $this->translator->trans(
             $request, 
diff --git a/docs/pages/docs/features/validation/http-controller-parameters/index.md b/docs/pages/docs/features/validation/http-controller-parameters/index.md
index ab9c1deff5d94c2836440517ac564cbc636d7edb..11f7a0736ca36d77b60b6d8dbcb7e87e272e5bd4 100644
--- a/docs/pages/docs/features/validation/http-controller-parameters/index.md
+++ b/docs/pages/docs/features/validation/http-controller-parameters/index.md
@@ -33,7 +33,7 @@ going to inject the data model into the parameter:
 ```php
 // ...
 
-public function handle(
+public function createResponse(
     #[ValidatedRequest(MyValidator::class)]
     MyValidatedData $data,
 ) {
@@ -82,8 +82,8 @@ use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Ds\Map;
 use Ds\Set;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::POST,
@@ -93,7 +93,7 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class BlogPostStore extends HttpController
 {
-    public function handle(
+    public function createResponse(
         #[ValidatedRequest(BlogPostFormValidator::class)]
         #[OnParameterResolution(
             status: HttpControllerParameterResolutionStatus::ValidationErrors,
@@ -109,11 +109,11 @@ final readonly class BlogPostStore extends HttpController
      * @param Map<string,Set<string>> $errors
      */
     public function handleValidationErrors(
-        Request $request,
-        Response $response,
+        ServerRequestInterface $request,
+        ResponseInterface $response,
         HttpControllerParameterResolution $resolution,
     ): HttpResponderInterface {
-        $response->status(400);
+        $response = $response->withStatus(400);
 
         /* render form with errors  */
         /* ... */
diff --git a/docs/pages/index.md b/docs/pages/index.md
index cd0fc5e27b5d6f6c25ae227f379db64fba690a3a..f532ee2da56514a1da75f65d86a4ff3094174cbe 100644
--- a/docs/pages/index.md
+++ b/docs/pages/index.md
@@ -202,9 +202,12 @@ final readonly class EchoResponder extends WebSocketRPCResponder
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 readonly class Homepage implements HttpResponderInterface
 {
-    public function respond(Request $request, Response $response): TwigTemplate
+    public function respond(
+        ServerRequestInterface $request, 
+        ResponseInterface $response
+    ): TwigTemplate
     {
-        return new TwigTemplate('website/homepage.twig');
+        return new TwigTemplate($request, $response, 'website/homepage.twig');
     }
 }</code></pre>
             </li>
diff --git a/docs/pages/tutorials/basic-graphql-schema/index.md b/docs/pages/tutorials/basic-graphql-schema/index.md
index 71c9cfbf3efac632dca7a1acca11e676ec333981..d18508c02c61c83cc90682e25322fdf153c3ba2a 100644
--- a/docs/pages/tutorials/basic-graphql-schema/index.md
+++ b/docs/pages/tutorials/basic-graphql-schema/index.md
@@ -73,8 +73,8 @@ use Distantmagic\Resonance\HttpResponderInterface;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\SiteAction;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::POST,
@@ -86,7 +86,7 @@ final readonly class GraphQL extends HttpResponder
 {
     public function __construct(private ResonanceGraphQL $graphql) {}
 
-    public function respond(Request $request, Response $response): HttpResponderInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpResponderInterface
     {
         return $this->graphql;
     }
diff --git a/docs/pages/tutorials/hello-world/index.md b/docs/pages/tutorials/hello-world/index.md
index 696d334ea865beb6d8165b4772ae046351b2e7fc..ed4684866afc2c4c7c80b8be0bd13cb194cfbfb6 100644
--- a/docs/pages/tutorials/hello-world/index.md
+++ b/docs/pages/tutorials/hello-world/index.md
@@ -102,8 +102,8 @@ use Distantmagic\Resonance\HttpResponder;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\TwigTemplate;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -113,9 +113,9 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class Homepage extends HttpResponder
 {
-    public function respond(Request $request, Response $response): HttpInterceptableInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
     {
-        return new TwigTemplate('homepage.twig');
+        return new TwigTemplate($request, $response, 'homepage.twig');
     }
 }
 ```
@@ -190,8 +190,8 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance;
 
 use Distantmagic\Resonance\Attribute\ContentSecurityPolicy;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[ContentSecurityPolicy(ContentSecurityPolicyType::Html)]
 final readonly class TwigTemplate implements HttpInterceptableInterface
@@ -201,7 +201,7 @@ final readonly class TwigTemplate implements HttpInterceptableInterface
         private array $templateData = [],
     ) {}
 
-    public function getTemplateData(Request $request, Response $response): array
+    public function getTemplateData(ServerRequestInterface $request, ResponseInterface $response): array
     {
         return $this->templateData + [
             'request' => $request,
diff --git a/docs/pages/tutorials/how-to-create-llm-websocket-chat-with-llama-cpp/index.md b/docs/pages/tutorials/how-to-create-llm-websocket-chat-with-llama-cpp/index.md
index db9fe2b2e2e6f6a3edbe2178ec43ab7003040801..811698d39274e468141e0e2750a336bba89d3ad1 100644
--- a/docs/pages/tutorials/how-to-create-llm-websocket-chat-with-llama-cpp/index.md
+++ b/docs/pages/tutorials/how-to-create-llm-websocket-chat-with-llama-cpp/index.md
@@ -98,8 +98,8 @@ use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\SiteAction;
 use Distantmagic\Resonance\TwigTemplate;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[Can(SiteAction::StartWebSocketRPCConnection)]
 #[RespondsToHttp(
@@ -111,9 +111,9 @@ use Swoole\Http\Response;
 #[WantsFeature(Feature::WebSocket)]
 final readonly class LlmChat extends HttpResponder
 {
-    public function respond(Request $request, Response $response): HttpInterceptableInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
     {
-        return new TwigTemplate('turbo/llmchat/index.twig');
+        return new TwigTemplate($request, $response, 'turbo/llmchat/index.twig');
     }
 }
 ```
diff --git a/docs/pages/tutorials/session-based-authentication/index.md b/docs/pages/tutorials/session-based-authentication/index.md
index 0b1f188adc9fe7a2b71b20b031bca181899fe812..7a127abafef1e40a98f124825921a61064d2e71f 100644
--- a/docs/pages/tutorials/session-based-authentication/index.md
+++ b/docs/pages/tutorials/session-based-authentication/index.md
@@ -159,7 +159,6 @@ use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\DoctrineEntityManagerRepository;
 use Distantmagic\Resonance\UserInterface;
 use Distantmagic\Resonance\UserRepositoryInterface;
-use Swoole\Http\Request;
 
 #[Singleton(provides: UserRepositoryInterface::class)]
 readonly class UserRepository implements UserRepositoryInterface
@@ -243,8 +242,8 @@ use Distantmagic\Resonance\HttpResponder;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\TwigTemplate;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -254,9 +253,9 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class LoginForm extends HttpResponder
 {
-    public function respond(Request $request, Response $response): HttpInterceptableInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
     {
-        return new TwigTemplate('auth/login_form.twig');
+        return new TwigTemplate($request, $response, 'auth/login_form.twig');
     }
 }
 ```
@@ -430,8 +429,8 @@ use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\TwigTemplate;
 use Doctrine\ORM\EntityRepository;
 use Ds\Map;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::POST,
@@ -449,14 +448,14 @@ final readonly class LoginValidation extends HttpController
         parent::__construct($controllerDependencies);
     }
 
-    public function handle(
-        Request $request,
-        Response $response,
+    public function createResponse(
+        ServerRequestInterface $request,
+        ResponseInterface $response,
         #[ValidatedRequest(UsernamePasswordValidator::class)]
         UsernamePassword $usernamePassword,
         #[DoctrineEntityRepository(User::class)]
         EntityRepository $users,
-    ): null|HttpInterceptableInterface {
+    ): HttpInterceptableInterface|ResponseInterface {
         /**
          * @var null|User
          */
@@ -465,9 +464,7 @@ final readonly class LoginValidation extends HttpController
         ]);
 
         if (!$user || !password_verify($usernamePassword->password, $user->getPasswordHash())) {
-            $response->status(403);
-
-            return;
+            return $response->withStatus(403);
         }
 
         $this->sessionAuthentication->setAuthenticatedUser(
@@ -476,7 +473,7 @@ final readonly class LoginValidation extends HttpController
             $user->user,
         );
 
-        return new InternalRedirect(HttpRouteSymbol::Homepage);
+        return new InternalRedirect($request, $response, HttpRouteSymbol::Homepage);
     }
 }
 ```
@@ -502,8 +499,8 @@ use Distantmagic\Resonance\HttpResponder;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SingletonCollection;
 use Distantmagic\Resonance\TwigTemplate;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::GET,
@@ -513,9 +510,9 @@ use Swoole\Http\Response;
 #[Singleton(collection: SingletonCollection::HttpResponder)]
 final readonly class LogoutForm extends HttpResponder
 {
-    public function respond(Request $request, Response $response): HttpInterceptableInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
     {
-        return new TwigTemplate('auth/logout_form.twig');
+        return new TwigTemplate($request, $response, 'auth/logout_form.twig');
     }
 }
 ```
@@ -552,8 +549,8 @@ use Distantmagic\Resonance\InternalRedirect;
 use Distantmagic\Resonance\RequestMethod;
 use Distantmagic\Resonance\SessionAuthentication;
 use Distantmagic\Resonance\SingletonCollection;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 #[RespondsToHttp(
     method: RequestMethod::POST,
@@ -568,13 +565,15 @@ final readonly class LogoutValidation extends HttpResponder
         private SessionAuthentication $sessionAuthentication,
     ) {}
 
-    public function respond(Request $request, Response $response): HttpInterceptableInterface
+    public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface
     {
         $this->sessionAuthentication->clearAuthenticatedUser($request);
 
-        $response->header('clear-site-data', '*');
-
-        return new InternalRedirect(HttpRouteSymbol::Homepage);
+        return new InternalRedirect(
+            $request,
+            $response->header('clear-site-data', '*');
+            HttpRouteSymbol::Homepage,
+        );
     }
 }
 ```
diff --git a/src/HttpInterceptor.php b/src/HttpInterceptor.php
index 25fa9d2d02bd74cc54c68373b8e64d964ac9e76b..d0aad021b042f22696f938ce4ecfe3945c356fd1 100644
--- a/src/HttpInterceptor.php
+++ b/src/HttpInterceptor.php
@@ -4,9 +4,18 @@ declare(strict_types=1);
 
 namespace Distantmagic\Resonance;
 
+use Psr\Http\Message\StreamInterface;
+use Stringable;
+
 /**
  * @template TClass
  *
  * @template-implements HttpInterceptorInterface<TClass>
  */
-abstract readonly class HttpInterceptor implements HttpInterceptorInterface {}
+abstract readonly class HttpInterceptor implements HttpInterceptorInterface
+{
+    public function createStream(string|Stringable $contents): StreamInterface
+    {
+        return new PsrStringStream($contents);
+    }
+}
diff --git a/src/HttpMiddleware/ValidatesCSRFTokenMiddleware.php b/src/HttpMiddleware/ValidatesCSRFTokenMiddleware.php
index 0e853a8ee53e453421c20c58977577a9a8694ec2..993c4052bd022abbbbad77078ba563c26f6768fb 100644
--- a/src/HttpMiddleware/ValidatesCSRFTokenMiddleware.php
+++ b/src/HttpMiddleware/ValidatesCSRFTokenMiddleware.php
@@ -5,10 +5,12 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\HttpMiddleware;
 
 use Distantmagic\Resonance\Attribute;
+use Distantmagic\Resonance\Attribute\GrantsFeature;
 use Distantmagic\Resonance\Attribute\HandlesMiddlewareAttribute;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Attribute\ValidatesCSRFToken;
 use Distantmagic\Resonance\CSRFManager;
+use Distantmagic\Resonance\Feature;
 use Distantmagic\Resonance\HttpInterceptableInterface;
 use Distantmagic\Resonance\HttpMiddleware;
 use Distantmagic\Resonance\HttpResponder\Error\BadRequest;
@@ -21,6 +23,7 @@ use Psr\Http\Message\ServerRequestInterface;
 /**
  * @template-extends HttpMiddleware<ValidatesCSRFToken>
  */
+#[GrantsFeature(Feature::HttpSession)]
 #[HandlesMiddlewareAttribute(
     attribute: ValidatesCSRFToken::class,
     priority: 1100,
diff --git a/src/HttpResponder.php b/src/HttpResponder.php
index 405288b202e13f5f236754d827f1fbef41227f2f..85b40c6d60c1addc2a536059161472c2c83507f3 100644
--- a/src/HttpResponder.php
+++ b/src/HttpResponder.php
@@ -4,14 +4,13 @@ declare(strict_types=1);
 
 namespace Distantmagic\Resonance;
 
-use LogicException;
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Stringable;
 
 abstract readonly class HttpResponder implements HttpResponderInterface
 {
-    public function handle(ServerRequestInterface $request): ResponseInterface
+    public function createStream(string|Stringable $contents): StreamInterface
     {
-        throw new LogicException('This method should not be called');
+        return new PsrStringStream($contents);
     }
 }
diff --git a/src/HttpResponder/Error/ServerError.php b/src/HttpResponder/Error/ServerError.php
index 171ddb5bb122da28c18239868f3b6e9025631c1f..7473ca60a45c43f054b6a8a0223ebf2845befbc7 100644
--- a/src/HttpResponder/Error/ServerError.php
+++ b/src/HttpResponder/Error/ServerError.php
@@ -13,7 +13,6 @@ use Distantmagic\Resonance\HttpError\ServerError as ServerErrorEntity;
 use Distantmagic\Resonance\HttpInterceptableInterface;
 use Distantmagic\Resonance\HttpResponder\Error;
 use Distantmagic\Resonance\HttpResponderInterface;
-use Distantmagic\Resonance\PsrStringStream;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Throwable;
@@ -41,7 +40,7 @@ final readonly class ServerError extends Error
         return $response
             ->withStatus(500)
             ->withHeader('content-type', ContentType::TextPlain->value)
-            ->withBody(new PsrStringStream((string) $throwable))
+            ->withBody($this->createStream($throwable))
         ;
     }
 }
diff --git a/src/HttpResponder/Json.php b/src/HttpResponder/Json.php
index 3eb39ec934adf9b3c20698a0fc483cef7b2df067..1b259d5081ec53cbb7fba2a83014a14eb80aa65c 100644
--- a/src/HttpResponder/Json.php
+++ b/src/HttpResponder/Json.php
@@ -6,7 +6,6 @@ namespace Distantmagic\Resonance\HttpResponder;
 
 use Distantmagic\Resonance\ContentType;
 use Distantmagic\Resonance\HttpResponder;
-use Distantmagic\Resonance\PsrStringStream;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 
@@ -19,7 +18,7 @@ readonly class Json extends HttpResponder
         return $response
             ->withStatus(200)
             ->withHeader('content-type', ContentType::ApplicationJson->value)
-            ->withBody(new PsrStringStream($this->json))
+            ->withBody($this->createStream($this->json))
         ;
     }
 }
diff --git a/src/HttpResponder/NotAcceptable.php b/src/HttpResponder/NotAcceptable.php
index ac97b8749c32cdffcbbcfbe5da36503c525c90d4..d28a2a7771233fc985e6c7234daf365b186064f7 100644
--- a/src/HttpResponder/NotAcceptable.php
+++ b/src/HttpResponder/NotAcceptable.php
@@ -7,7 +7,6 @@ namespace Distantmagic\Resonance\HttpResponder;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\ContentType;
 use Distantmagic\Resonance\HttpResponder;
-use Distantmagic\Resonance\PsrStringStream;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 
@@ -19,7 +18,7 @@ final readonly class NotAcceptable extends HttpResponder
         return $response
             ->withStatus(406)
             ->withHeader('content-type', ContentType::TextPlain->value)
-            ->withBody(new PsrStringStream('406'))
+            ->withBody($this->createStream('406'))
         ;
     }
 }
diff --git a/src/HttpResponderInterface.php b/src/HttpResponderInterface.php
index 4947b4a8120eca436bd194280e2b449f2a076bc9..a9bd5700793a02e1fa11797b7fc5aa130963f3fb 100644
--- a/src/HttpResponderInterface.php
+++ b/src/HttpResponderInterface.php
@@ -6,9 +6,8 @@ namespace Distantmagic\Resonance;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Server\RequestHandlerInterface;
 
-interface HttpResponderInterface extends RequestHandlerInterface
+interface HttpResponderInterface
 {
     public function respond(ServerRequestInterface $request, ResponseInterface $response): HttpInterceptableInterface|ResponseInterface|self;
 }
diff --git a/src/PsrStringStream.php b/src/PsrStringStream.php
index 003a8ac55d4eb7e6a7c4e7f4227b8984e3e8c5a0..8c9dc8a344d2e09d7cadf224e18d4497f12f202b 100644
--- a/src/PsrStringStream.php
+++ b/src/PsrStringStream.php
@@ -5,10 +5,16 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance;
 
 use Psr\Http\Message\StreamInterface;
+use Stringable;
 
 readonly class PsrStringStream implements StreamInterface
 {
-    public function __construct(private string $contents) {}
+    private string $contents;
+
+    public function __construct(string|Stringable $contents)
+    {
+        $this->contents = (string) $contents;
+    }
 
     public function __toString(): string
     {