diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b0e4f6a..1231aa2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,7 +3,9 @@ name: CI
 on:
   push:
     branches:
-      - 2.x
+      - '[0-9]+.x'
+      - '[0-9]+.[0-9]+'
+      - '[0-9]+.[0-9]+.x'
   pull_request:
 
 jobs:
@@ -91,7 +93,7 @@ jobs:
       - name: Setup PHP
         uses: shivammathur/setup-php@v2
         with:
-          php-version: 7.4
+          php-version: 8.3
           tools: composer
           coverage: xdebug
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b030fcb..b766856 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Change Log
 
+## 2.3.0
+
+ * Fixed compatibility with `psr/http-message` v2
+ * The `Http\Client\Socket\Stream` has BC breaks if you extended it. It is not meant to be extended, declaring it as `@internal` now.
+
 ## 2.2.0
 
  * Allow installation with Symfony 7
diff --git a/README.md b/README.md
index 193ff9b..27be400 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ First launch the http server:
 $ ./vendor/bin/http_test_server > /dev/null 2>&1 &
 ```
 
-Then generate ssh certificates:
+Then generate SSL certificates:
 
 ```bash
 $ composer gen-ssl
diff --git a/composer.json b/composer.json
index 11542e4..0689754 100644
--- a/composer.json
+++ b/composer.json
@@ -9,18 +9,20 @@
         }
     ],
     "require": {
-        "php": "^7.2 || ^8.0",
-        "nyholm/psr7": "^1.3",
-        "php-http/httplug": "^2.0",
+        "php": "^8.1",
+        "nyholm/psr7": "^1.8.1",
+        "php-http/httplug": "^2.4",
         "psr/http-client": "^1.0",
+        "psr/http-message": "^1.0 || ^2.0",
         "symfony/options-resolver": "^2.6 || ^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0"
     },
     "require-dev": {
-        "friendsofphp/php-cs-fixer": "^2.2 || ^3.0",
-        "php-http/client-integration-tests": "^3.0",
-        "php-http/message": "^1.9",
-        "php-http/client-common": "^2.3",
-        "phpunit/phpunit": "^8.5.23 || ~9.5"
+        "friendsofphp/php-cs-fixer": "^3.51",
+        "php-http/client-integration-tests": "^3.1.1",
+        "php-http/message": "^1.16",
+        "php-http/client-common": "^2.7",
+        "phpunit/phpunit": "^8.5.23 || ~9.5",
+        "php-http/message-factory": "^1.1"
     },
     "provide": {
         "php-http/client-implementation": "1.0",
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 034d22e..f12146d 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -8,7 +8,6 @@
   <testsuites>
     <testsuite name="Socket Client Test Suite">
       <directory>tests/</directory>
-      <exclude>tests/SocketClientFeatureTest.php</exclude>
     </testsuite>
   </testsuites>
   <php>
diff --git a/src/ResponseReader.php b/src/ResponseReader.php
index 66431b0..d5beae6 100644
--- a/src/ResponseReader.php
+++ b/src/ResponseReader.php
@@ -4,7 +4,6 @@
 
 use Http\Client\Socket\Exception\BrokenPipeException;
 use Http\Client\Socket\Exception\TimeoutException;
-use Http\Message\ResponseFactory;
 use Nyholm\Psr7\Response;
 use Psr\Http\Message\RequestInterface;
 use Psr\Http\Message\ResponseInterface;
@@ -18,11 +17,6 @@
  */
 trait ResponseReader
 {
-    /**
-     * @var ResponseFactory For creating response
-     */
-    protected $responseFactory;
-
     /**
      * Read a response from a socket.
      *
diff --git a/src/Stream.php b/src/Stream.php
index 8d26ca0..afe6dd6 100644
--- a/src/Stream.php
+++ b/src/Stream.php
@@ -61,7 +61,7 @@ public function __construct(RequestInterface $request, $socket, ?int $size = nul
         $this->request = $request;
     }
 
-    public function __toString()
+    public function __toString(): string
     {
         try {
             return $this->getContents();
@@ -70,7 +70,7 @@ public function __toString()
         }
     }
 
-    public function close()
+    public function close(): void
     {
         if ($this->isDetached || null === $this->socket) {
             throw new StreamException('Stream is detached');
@@ -93,12 +93,12 @@ public function detach()
     /**
      * @return int<0, max>|null
      */
-    public function getSize()
+    public function getSize(): ?int
     {
         return $this->size;
     }
 
-    public function tell()
+    public function tell(): int
     {
         if ($this->isDetached || null === $this->socket) {
             throw new StreamException('Stream is detached');
@@ -111,7 +111,7 @@ public function tell()
         return $tell;
     }
 
-    public function eof()
+    public function eof(): bool
     {
         if ($this->isDetached || null === $this->socket) {
             throw new StreamException('Stream is detached');
@@ -120,38 +120,32 @@ public function eof()
         return feof($this->socket);
     }
 
-    public function isSeekable()
+    public function isSeekable(): bool
     {
         return false;
     }
 
-    /**
-     * @return void
-     */
-    public function seek($offset, $whence = SEEK_SET)
+    public function seek($offset, $whence = SEEK_SET): void
     {
         throw new StreamException('This stream is not seekable');
     }
 
-    /**
-     * @return void
-     */
-    public function rewind()
+    public function rewind(): void
     {
         throw new StreamException('This stream is not seekable');
     }
 
-    public function isWritable()
+    public function isWritable(): bool
     {
         return false;
     }
 
-    public function write($string)
+    public function write($string): int
     {
         throw new StreamException('This stream is not writable');
     }
 
-    public function isReadable()
+    public function isReadable(): bool
     {
         return true;
     }
@@ -159,11 +153,16 @@ public function isReadable()
     /**
      * @param int<0, max> $length
      */
-    public function read($length)
+    public function read($length): string
     {
+        if (0 === $length) {
+            return '';
+        }
+
         if ($this->isDetached || null === $this->socket) {
             throw new StreamException('Stream is detached');
         }
+
         if (null === $this->getSize()) {
             $read = fread($this->socket, $length);
             if (false === $read) {
@@ -197,7 +196,7 @@ public function read($length)
         return $read;
     }
 
-    public function getContents()
+    public function getContents(): string
     {
         if ($this->isDetached || null === $this->socket) {
             throw new StreamException('Stream is detached');
diff --git a/tests/SocketClientFeatureTest.php b/tests/SocketClientFeatureTest.php
index e6e698b..f1fe854 100644
--- a/tests/SocketClientFeatureTest.php
+++ b/tests/SocketClientFeatureTest.php
@@ -12,4 +12,29 @@ protected function createClient(): ClientInterface
     {
         return new SocketHttpClient();
     }
+
+    public function testAutoSetContentLength(): void
+    {
+        $this->markTestSkipped('Feature is unsupported');
+    }
+
+    public function testGzip(): void
+    {
+        $this->markTestSkipped('Feature is unsupported');
+    }
+
+    public function testDeflate(): void
+    {
+        $this->markTestSkipped('Feature is unsupported');
+    }
+
+    public function testChunked(): void
+    {
+        $this->markTestSkipped('Feature is unsupported');
+    }
+
+    public function testRedirect(): void
+    {
+        $this->markTestSkipped('Feature is unsupported');
+    }
 }
diff --git a/tests/SocketHttpClientTest.php b/tests/SocketHttpClientTest.php
index c3e5da2..657f2ff 100644
--- a/tests/SocketHttpClientTest.php
+++ b/tests/SocketHttpClientTest.php
@@ -6,15 +6,13 @@
 use Http\Client\Socket\Client as SocketHttpClient;
 use Http\Client\Socket\Exception\NetworkException;
 use Http\Client\Socket\Exception\TimeoutException;
-use Http\Message\MessageFactory\GuzzleMessageFactory;
+use Nyholm\Psr7\Factory\Psr17Factory;
 
 class SocketHttpClientTest extends BaseTestCase
 {
     public function createClient($options = [])
     {
-        $messageFactory = new GuzzleMessageFactory();
-
-        return new HttpMethodsClient(new SocketHttpClient($options), $messageFactory);
+        return new HttpMethodsClient(new SocketHttpClient($options), new Psr17Factory());
     }
 
     public function testTcpSocketDomain()