17
17
use Bolt \Bolt ;
18
18
use Bolt \connection \StreamSocket ;
19
19
use Exception ;
20
- use function explode ;
21
- use const FILTER_VALIDATE_IP ;
22
- use function filter_var ;
23
20
use Laudis \Neo4j \Common \BoltConnection ;
24
21
use Laudis \Neo4j \Contracts \AuthenticateInterface ;
25
22
use Laudis \Neo4j \Contracts \ConnectionInterface ;
26
23
use Laudis \Neo4j \Contracts \ConnectionPoolInterface ;
27
24
use Laudis \Neo4j \Databags \DatabaseInfo ;
25
+ use Laudis \Neo4j \Databags \DriverConfiguration ;
28
26
use Laudis \Neo4j \Databags \SessionConfiguration ;
29
27
use Laudis \Neo4j \Enum \ConnectionProtocol ;
30
28
use Laudis \Neo4j \Neo4j \RoutingTable ;
39
37
*/
40
38
final class BoltConnectionPool implements ConnectionPoolInterface
41
39
{
42
- /** @var array<string, list<ConnectionInterface<Bolt> >> */
40
+ /** @var array<string, list<BoltConnection >> */
43
41
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
+ }
44
53
45
54
/**
46
55
* @throws Exception
@@ -60,8 +69,20 @@ public function acquire(
60
69
self ::$ connectionCache [$ key ] = [];
61
70
}
62
71
63
- foreach (self ::$ connectionCache [$ key ] as $ connection ) {
72
+ foreach (self ::$ connectionCache [$ key ] as $ i => $ connection ) {
64
73
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
+ }
65
86
$ connection ->open ();
66
87
67
88
$ authenticate ->authenticateBolt ($ connection ->getImplementation (), $ connectingTo , $ userAgent );
@@ -70,9 +91,42 @@ public function acquire(
70
91
}
71
92
}
72
93
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 {
73
127
$ socket = new StreamSocket ($ connectingTo ->getHost (), $ connectingTo ->getPort () ?? 7687 , $ socketTimeout );
74
128
75
- $ this ->configureSsl ($ uri , $ connectingTo , $ socket , $ table );
129
+ $ this ->setupSsl ($ uri , $ connectingTo , $ table , $ socket );
76
130
77
131
$ bolt = new Bolt ($ socket );
78
132
$ authenticate ->authenticateBolt ($ bolt , $ connectingTo , $ userAgent );
@@ -104,6 +158,7 @@ public function acquire(
104
158
ConnectionProtocol::determineBoltVersion ($ bolt ),
105
159
$ config ->getAccessMode (),
106
160
new DatabaseInfo ($ config ->getDatabase ()),
161
+ $ this ->driverConfig ,
107
162
static function () use ($ socket , $ authenticate , $ connectingTo , $ userAgent , $ originalBolt ) {
108
163
$ bolt = $ originalBolt ->get ();
109
164
if ($ bolt === null ) {
@@ -117,61 +172,14 @@ static function () use ($socket, $authenticate, $connectingTo, $userAgent, $orig
117
172
118
173
$ connection ->open ();
119
174
120
- self ::$ connectionCache [$ key ][] = $ connection ;
121
-
122
175
return $ connection ;
123
176
}
124
177
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
126
179
{
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 );
141
183
}
142
184
}
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
- }
177
185
}
0 commit comments