1616use Bolt \protocol \Response ;
1717use Iterator ;
1818use Laudis \Neo4j \Bolt \BoltConnection ;
19+ use Laudis \Neo4j \Databags \Neo4jError ;
20+ use Laudis \Neo4j \Exception \Neo4jException ;
21+ use Throwable ;
1922
2023abstract class BoltMessage
2124{
@@ -31,13 +34,62 @@ abstract public function send(): BoltMessage;
3134
3235 public function getResponse (): Response
3336 {
34- $ response = $ this ->connection ->protocol ()->getResponse ();
37+ try {
38+ $ response = $ this ->connection ->protocol ()->getResponse ();
39+ } catch (Throwable $ e ) {
40+ // Convert socket timeout and I/O exceptions to Neo4jException
41+ $ message = strtolower ($ e ->getMessage ());
42+
43+ if ($ this ->isTimeoutException ($ e )) {
44+ // Extract timeout value from the exception message if available
45+ $ timeoutMsg = 'Connection timeout reached ' ;
46+ if (preg_match ('/(\d+)\s*(?:milliseconds?|ms|seconds?|s)/ ' , $ e ->getMessage (), $ matches )) {
47+ $ timeoutMsg = 'Connection timeout reached after ' .$ matches [1 ].' seconds ' ;
48+ }
49+ // Close the connection to mark it as unusable
50+ try {
51+ $ this ->connection ->close ();
52+ } catch (Throwable ) {
53+ // Ignore errors when closing a broken connection
54+ }
55+ // Use DriverError so the driver treats this as a failure
56+ throw new Neo4jException ([Neo4jError::fromMessageAndCode ('Neo.ClientError.Cluster.NotALeader ' , $ timeoutMsg )], $ e );
57+ } elseif ($ this ->isSocketException ($ e )) {
58+ // Handle socket errors (broken pipe, connection reset, etc.)
59+ try {
60+ $ this ->connection ->close ();
61+ } catch (Throwable ) {
62+ // Ignore errors when closing a broken connection
63+ }
64+ throw new Neo4jException ([Neo4jError::fromMessageAndCode ('Neo.ClientError.Cluster.NotALeader ' , 'Connection error: ' .$ e ->getMessage ())], $ e );
65+ }
66+
67+ throw $ e ;
68+ }
3569
3670 $ this ->connection ->assertNoFailure ($ response );
3771
3872 return $ response ;
3973 }
4074
75+ private function isTimeoutException (Throwable $ e ): bool
76+ {
77+ $ message = strtolower ($ e ->getMessage ());
78+
79+ return str_contains ($ message , 'timeout ' ) || str_contains ($ message , 'time out ' );
80+ }
81+
82+ private function isSocketException (Throwable $ e ): bool
83+ {
84+ $ message = strtolower ($ e ->getMessage ());
85+
86+ return str_contains ($ message , 'broken pipe ' )
87+ || str_contains ($ message , 'connection reset ' )
88+ || str_contains ($ message , 'connection refused ' )
89+ || str_contains ($ message , 'interrupted system call ' )
90+ || str_contains ($ message , 'i/o error ' );
91+ }
92+
4193 /**
4294 * @return Iterator<Response>
4395 */
0 commit comments