44
55use Bolt \Bolt ;
66use Bolt \error \ConnectException ;
7+ use Bolt \error \ConnectionTimeoutException ;
8+ use function microtime ;
9+ use function round ;
10+ use function socket_get_status ;
11+ use function socket_strerror ;
712
813/**
914 * Socket class
@@ -20,6 +25,10 @@ class Socket extends AConnection
2025 */
2126 private $ socket = false ;
2227
28+ private const RESOURCE_UNAVAILABLE_CODE = 11 ;
29+ /** @var float|null */
30+ private $ timetAtTimeoutConfiguration ;
31+
2332 /**
2433 * Create socket connection
2534 * @return bool
@@ -73,8 +82,7 @@ public function write(string $buffer)
7382 while (0 < $ size ) {
7483 $ sent = socket_write ($ this ->socket , $ buffer , $ size );
7584 if ($ sent === false ) {
76- $ code = socket_last_error ($ this ->socket );
77- throw new ConnectException (socket_strerror ($ code ), $ code );
85+ $ this ->throwConnectException ();
7886 }
7987
8088 $ buffer = mb_strcut ($ buffer , $ sent , null , '8bit ' );
@@ -99,8 +107,7 @@ public function read(int $length = 2048): string
99107 do {
100108 $ readed = socket_read ($ this ->socket , $ length - mb_strlen ($ output , '8bit ' ), PHP_BINARY_READ );
101109 if ($ readed === false ) {
102- $ code = socket_last_error ($ this ->socket );
103- throw new ConnectException (socket_strerror ($ code ), $ code );
110+ $ this ->throwConnectException ();
104111 }
105112 $ output .= $ readed ;
106113 } while (mb_strlen ($ output , '8bit ' ) < $ length );
@@ -135,5 +142,23 @@ private function configureTimeout(): void
135142 $ timeoutOption = ['sec ' => $ timeoutSeconds , 'usec ' => $ microSeconds ];
136143 socket_set_option ($ this ->socket , SOL_SOCKET , SO_RCVTIMEO , $ timeoutOption );
137144 socket_set_option ($ this ->socket , SOL_SOCKET , SO_SNDTIMEO , $ timeoutOption );
145+ $ this ->timetAtTimeoutConfiguration = microtime (true );
146+ }
147+
148+ /**
149+ * @return mixed
150+ * @throws ConnectException
151+ * @throws ConnectionTimeoutException
152+ */
153+ private function throwConnectException ()
154+ {
155+ $ code = socket_last_error ($ this ->socket );
156+ if ($ code === self ::RESOURCE_UNAVAILABLE_CODE ) {
157+ $ timediff = microtime (true ) - $ this ->timetAtTimeoutConfiguration ;
158+ if ($ timediff >= $ this ->timeout ) {
159+ throw ConnectionTimeoutException::createFromTimeout ($ this ->timeout );
160+ }
161+ }
162+ throw new ConnectException (socket_strerror ($ code ), $ code );
138163 }
139164}
0 commit comments