forked from eclipse-vertx/vertx-sql-client
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMSSQLConnectionFactory.java
More file actions
164 lines (141 loc) · 7.2 KB
/
MSSQLConnectionFactory.java
File metadata and controls
164 lines (141 loc) · 7.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
* Copyright (c) 2011-2026 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package io.vertx.mssqlclient.impl;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.internal.ContextInternal;
import io.vertx.core.internal.VertxInternal;
import io.vertx.core.internal.net.NetSocketInternal;
import io.vertx.core.internal.tls.ClientSslContextManager;
import io.vertx.core.internal.tls.SslContextManager;
import io.vertx.core.net.*;
import io.vertx.core.spi.metrics.ClientMetrics;
import io.vertx.core.spi.metrics.VertxMetrics;
import io.vertx.mssqlclient.EncryptionMode;
import io.vertx.mssqlclient.MSSQLConnectOptions;
import io.vertx.sqlclient.impl.ConnectionFactoryBase;
import io.vertx.sqlclient.spi.connection.Connection;
import java.util.List;
import java.util.Map;
import static io.vertx.mssqlclient.impl.codec.EncryptionLevel.*;
public class MSSQLConnectionFactory extends ConnectionFactoryBase<MSSQLConnectOptions> {
private static final List<String> TDS_PROTOCOLS = List.of("tds/8.0");
private final ClientSslContextManager sslContextManager;
public MSSQLConnectionFactory(VertxInternal vertx) {
this(vertx, new NetClientOptions());
}
public MSSQLConnectionFactory(VertxInternal vertx, NetClientOptions transportOptions) {
super(vertx, transportOptions);
sslContextManager = new ClientSslContextManager(SslContextManager.resolveEngineOptions(tcpOptions.getSslEngineOptions(), tcpOptions.isUseAlpn()));
}
@Override
protected Future<Connection> doConnectInternal(MSSQLConnectOptions options, ContextInternal context) {
return connectOrRedirect(options, context, 0);
}
private Future<Connection> connectOrRedirect(MSSQLConnectOptions options, ContextInternal context, int redirections) {
if (redirections > 1) {
return context.failedFuture("The client can be redirected only once");
}
EncryptionMode encryptionMode = options.getEncryptionMode();
if (encryptionMode == EncryptionMode.STRICT) {
return connectWithTds8(options, context, redirections);
} else {
return connectWithTds7x(options, context, redirections);
}
}
private Future<Connection> connectWithTds8(MSSQLConnectOptions options, ContextInternal context, int redirections) {
SocketAddress server = options.getSocketAddress();
ClientSSLOptions sslOptions = copyClientSSLOptions(options.getSslOptions());
if (sslOptions.isTrustAll()) {
return context.failedFuture("Strict encryption mode requires proper certificate validation. Configure SSL options with valid certificates.");
}
if (sslOptions.getHostnameVerificationAlgorithm() == null) {
sslOptions.setHostnameVerificationAlgorithm("");
}
sslOptions.setUseAlpn(true).setApplicationLayerProtocols(TDS_PROTOCOLS);
ConnectOptions connectOpts = new ConnectOptions()
.setRemoteAddress(server)
.setSsl(true)
.setSslOptions(sslOptions);
return client.connect(connectOpts)
.map(so -> createSocketConnection(so, options, context))
.compose(conn -> conn.sendPreLoginMessage(false)
.compose(encryptionLevel -> sendLogin(conn, options)))
.compose(connBase -> handleRedirectionToAlternateServer(connBase, options, context, redirections));
}
private ClientSSLOptions copyClientSSLOptions(ClientSSLOptions sslOptions) {
return sslOptions == null ? new ClientSSLOptions() : sslOptions.copy();
}
private Future<Connection> connectWithTds7x(MSSQLConnectOptions options, ContextInternal context, int redirections) {
SocketAddress server = options.getSocketAddress();
boolean clientSslConfig = options.isSsl();
// Always start unencrypted, the connection will be upgraded if client and server agree
return client.connect(server)
.map(so -> createSocketConnection(so, options, context))
.compose(conn -> conn.sendPreLoginMessage(clientSslConfig)
.compose(encryptionLevel -> loginTds7x(conn, options, encryptionLevel, context)))
.compose(connBase -> handleRedirectionToAlternateServer(connBase, options, context, redirections));
}
private Future<Connection> sendLogin(MSSQLSocketConnection conn, MSSQLConnectOptions options) {
String username = options.getUser();
String password = options.getPassword();
String database = options.getDatabase();
Map<String, String> properties = options.getProperties();
return conn.sendLoginMessage(username, password, database, properties);
}
private Future<Connection> handleRedirectionToAlternateServer(Connection connBase, MSSQLConnectOptions options, ContextInternal context, int redirections) {
MSSQLSocketConnection conn = (MSSQLSocketConnection) connBase;
HostAndPort alternateServer = conn.getAlternateServer();
if (alternateServer == null) {
return context.succeededFuture(conn);
}
Promise<Void> closePromise = context.promise();
conn.close(null, closePromise);
return closePromise.future().transform(v -> {
MSSQLConnectOptions connectOptions = new MSSQLConnectOptions(options)
.setHost(alternateServer.host())
.setPort(alternateServer.port());
return connectOrRedirect(connectOptions, context, redirections + 1);
});
}
private MSSQLSocketConnection createSocketConnection(NetSocket so, MSSQLConnectOptions options, ContextInternal context) {
VertxMetrics vertxMetrics = vertx.metrics();
ClientMetrics metrics = vertxMetrics != null ? vertxMetrics.createClientMetrics(options.getSocketAddress(), "sql", options.getMetricsName()) : null;
MSSQLSocketConnection conn = new MSSQLSocketConnection((NetSocketInternal) so, sslContextManager, metrics, options, false, 0, sql -> true, 1, context);
conn.init();
return conn;
}
private Future<Connection> loginTds7x(MSSQLSocketConnection conn, MSSQLConnectOptions options, byte encryptionLevel, ContextInternal context) {
boolean clientSslConfig = options.isSsl();
if (clientSslConfig && encryptionLevel != ENCRYPT_ON && encryptionLevel != ENCRYPT_REQ) {
Promise<Void> closePromise = context.promise();
conn.close(null, closePromise);
return closePromise.future().transform(v -> context.failedFuture("The client is configured for encryption but the server does not support it"));
}
Future<Void> future;
if (encryptionLevel != ENCRYPT_NOT_SUP) {
// Start connection encryption ...
future = conn.enableSslForTds7x(clientSslConfig, encryptionLevel, options);
} else {
// ... unless the client did not require encryption and the server does not support it
future = context.succeededFuture();
}
return future.compose(v -> sendLogin(conn, options));
}
@Override
public Future<Connection> connect(Context context, MSSQLConnectOptions options) {
ContextInternal ctx = (ContextInternal) context;
Promise<Connection> promise = ctx.promise();
connect(asEventLoopContext(ctx), options).onComplete(promise);
return promise.future();
}
}