1717use Bolt \Bolt ;
1818use Bolt \connection \StreamSocket ;
1919use Exception ;
20- use function explode ;
21- use const FILTER_VALIDATE_IP ;
22- use function filter_var ;
2320use Laudis \Neo4j \Common \BoltConnection ;
2421use Laudis \Neo4j \Contracts \AuthenticateInterface ;
2522use Laudis \Neo4j \Contracts \ConnectionInterface ;
2623use Laudis \Neo4j \Contracts \ConnectionPoolInterface ;
2724use Laudis \Neo4j \Databags \DatabaseInfo ;
25+ use Laudis \Neo4j \Databags \DriverConfiguration ;
2826use Laudis \Neo4j \Databags \SessionConfiguration ;
2927use Laudis \Neo4j \Enum \ConnectionProtocol ;
3028use Laudis \Neo4j \Neo4j \RoutingTable ;
3937 */
4038final class BoltConnectionPool implements ConnectionPoolInterface
4139{
42- /** @var array<string, list<ConnectionInterface<Bolt> >> */
40+ /** @var array<string, list<BoltConnection >> */
4341 private static array $ connectionCache = [];
42+ private DriverConfiguration $ driverConfig ;
43+ private SslConfigurator $ sslConfigurator ;
44+
45+ /**
46+ * @psalm-external-mutation-free
47+ */
48+ public function __construct (DriverConfiguration $ driverConfig , SslConfigurator $ sslConfigurator )
49+ {
50+ $ this ->driverConfig = $ driverConfig ;
51+ $ this ->sslConfigurator = $ sslConfigurator ;
52+ }
4453
4554 /**
4655 * @throws Exception
@@ -60,8 +69,20 @@ public function acquire(
6069 self ::$ connectionCache [$ key ] = [];
6170 }
6271
63- foreach (self ::$ connectionCache [$ key ] as $ connection ) {
72+ foreach (self ::$ connectionCache [$ key ] as $ i => $ connection ) {
6473 if (!$ connection ->isOpen ()) {
74+ $ sslConfig = $ connection ->getDriverConfiguration ()->getSslConfiguration ();
75+ $ newSslConfig = $ this ->driverConfig ->getSslConfiguration ();
76+ if ($ sslConfig ->getMode () !== $ newSslConfig ->getMode () ||
77+ $ sslConfig ->isVerifyPeer () === $ newSslConfig ->isVerifyPeer ()
78+ ) {
79+ $ connection = $ this ->openConnection ($ connectingTo , $ socketTimeout , $ uri , $ table , $ authenticate , $ userAgent , $ config );
80+
81+ /** @psalm-suppress PropertyTypeCoercion */
82+ self ::$ connectionCache [$ key ][$ i ] = $ connection ;
83+
84+ return $ connection ;
85+ }
6586 $ connection ->open ();
6687
6788 $ authenticate ->authenticateBolt ($ connection ->getImplementation (), $ connectingTo , $ userAgent );
@@ -70,9 +91,42 @@ public function acquire(
7091 }
7192 }
7293
94+ $ connection = $ this ->openConnection ($ connectingTo , $ socketTimeout , $ uri , $ table , $ authenticate , $ userAgent , $ config );
95+
96+ self ::$ connectionCache [$ key ][] = $ connection ;
97+
98+ return $ connection ;
99+ }
100+
101+ public function canConnect (UriInterface $ uri , AuthenticateInterface $ authenticate , ?RoutingTable $ table = null , ?UriInterface $ server = null ): bool
102+ {
103+ $ connectingTo = $ server ?? $ uri ;
104+ $ socket = new StreamSocket ($ uri ->getHost (), $ connectingTo ->getPort () ?? 7687 );
105+
106+ $ this ->setupSsl ($ uri , $ connectingTo , $ table , $ socket );
107+
108+ try {
109+ $ bolt = new Bolt ($ socket );
110+ $ authenticate ->authenticateBolt ($ bolt , $ connectingTo , 'ping ' );
111+ } catch (Throwable $ e ) {
112+ return false ;
113+ }
114+
115+ return true ;
116+ }
117+
118+ private function openConnection (
119+ UriInterface $ connectingTo ,
120+ float $ socketTimeout ,
121+ UriInterface $ uri ,
122+ ?RoutingTable $ table ,
123+ AuthenticateInterface $ authenticate ,
124+ string $ userAgent ,
125+ SessionConfiguration $ config
126+ ): BoltConnection {
73127 $ socket = new StreamSocket ($ connectingTo ->getHost (), $ connectingTo ->getPort () ?? 7687 , $ socketTimeout );
74128
75- $ this ->configureSsl ($ uri , $ connectingTo , $ socket , $ table );
129+ $ this ->setupSsl ($ uri , $ connectingTo , $ table , $ socket );
76130
77131 $ bolt = new Bolt ($ socket );
78132 $ authenticate ->authenticateBolt ($ bolt , $ connectingTo , $ userAgent );
@@ -104,6 +158,7 @@ public function acquire(
104158 ConnectionProtocol::determineBoltVersion ($ bolt ),
105159 $ config ->getAccessMode (),
106160 new DatabaseInfo ($ config ->getDatabase ()),
161+ $ this ->driverConfig ,
107162 static function () use ($ socket , $ authenticate , $ connectingTo , $ userAgent , $ originalBolt ) {
108163 $ bolt = $ originalBolt ->get ();
109164 if ($ bolt === null ) {
@@ -117,61 +172,14 @@ static function () use ($socket, $authenticate, $connectingTo, $userAgent, $orig
117172
118173 $ connection ->open ();
119174
120- self ::$ connectionCache [$ key ][] = $ connection ;
121-
122175 return $ connection ;
123176 }
124177
125- private function configureSsl (UriInterface $ uri , UriInterface $ server , StreamSocket $ socket , ? RoutingTable $ table ): void
178+ private function setupSsl (UriInterface $ uri , UriInterface $ connectingTo , ? RoutingTable $ table , StreamSocket $ socket ): void
126179 {
127- $ scheme = $ uri ->getScheme ();
128- $ explosion = explode ('+ ' , $ scheme , 2 );
129- $ sslConfig = $ explosion [1 ] ?? '' ;
130-
131- if (str_starts_with ($ sslConfig , 's ' )) {
132- // We have to pass a different host when working with ssl on aura.
133- // There is a strange behaviour where if we pass the uri host on a single
134- // instance aura deployment, we need to pass the original uri for the
135- // ssl configuration to be valid.
136- if ($ table && count ($ table ->getWithRole ()) > 1 ) {
137- $ this ->enableSsl ($ server ->getHost (), $ sslConfig , $ socket );
138- } else {
139- $ this ->enableSsl ($ uri ->getHost (), $ sslConfig , $ socket );
140- }
180+ $ config = $ this ->sslConfigurator ->configure ($ uri , $ connectingTo , $ table , $ this ->driverConfig );
181+ if ($ config !== null ) {
182+ $ socket ->setSslContextOptions ($ config );
141183 }
142184 }
143-
144- private function enableSsl (string $ host , string $ sslConfig , StreamSocket $ sock ): void
145- {
146- $ options = [
147- 'verify_peer ' => true ,
148- 'peer_name ' => $ host ,
149- ];
150- if (!filter_var ($ host , FILTER_VALIDATE_IP )) {
151- $ options ['SNI_enabled ' ] = true ;
152- }
153- if ($ sslConfig === 's ' ) {
154- $ sock ->setSslContextOptions ($ options );
155- } elseif ($ sslConfig === 'ssc ' ) {
156- $ options ['allow_self_signed ' ] = true ;
157- $ sock ->setSslContextOptions ($ options );
158- }
159- }
160-
161- public function canConnect (UriInterface $ uri , AuthenticateInterface $ authenticate , ?RoutingTable $ table = null , ?UriInterface $ server = null ): bool
162- {
163- $ connectingTo = $ server ?? $ uri ;
164- $ socket = new StreamSocket ($ uri ->getHost (), $ connectingTo ->getPort () ?? 7687 );
165-
166- $ this ->configureSsl ($ uri , $ connectingTo , $ socket , $ table );
167-
168- try {
169- $ bolt = new Bolt ($ socket );
170- $ authenticate ->authenticateBolt ($ bolt , $ connectingTo , 'ping ' );
171- } catch (Throwable $ e ) {
172- return false ;
173- }
174-
175- return true ;
176- }
177185}
0 commit comments