3131import com .datastax .oss .driver .internal .core .util .concurrent .UncaughtExceptions ;
3232import com .datastax .oss .protocol .internal .Message ;
3333import com .datastax .oss .protocol .internal .ProtocolConstants ;
34+ import com .datastax .oss .protocol .internal .ProtocolConstants .ErrorCode ;
3435import com .datastax .oss .protocol .internal .request .AuthResponse ;
36+ import com .datastax .oss .protocol .internal .request .Options ;
3537import com .datastax .oss .protocol .internal .request .Query ;
3638import com .datastax .oss .protocol .internal .request .Register ;
3739import com .datastax .oss .protocol .internal .request .Startup ;
4042import com .datastax .oss .protocol .internal .response .Authenticate ;
4143import com .datastax .oss .protocol .internal .response .Error ;
4244import com .datastax .oss .protocol .internal .response .Ready ;
45+ import com .datastax .oss .protocol .internal .response .Supported ;
4346import com .datastax .oss .protocol .internal .response .result .Rows ;
4447import com .datastax .oss .protocol .internal .response .result .SetKeyspace ;
4548import io .netty .channel .ChannelHandlerContext ;
@@ -69,14 +72,21 @@ class ProtocolInitHandler extends ConnectInitHandler {
6972 private final HeartbeatHandler heartbeatHandler ;
7073 private String logPrefix ;
7174 private ChannelHandlerContext ctx ;
75+ private boolean querySupportedOptions ;
7276
77+ /**
78+ * @param querySupportedOptions whether to send OPTIONS as the first message, to request which
79+ * protocol options the channel supports. If this is true, the options will be stored as a
80+ * channel attribute, and exposed via {@link DriverChannel#getOptions()}.
81+ */
7382 ProtocolInitHandler (
7483 InternalDriverContext context ,
7584 ProtocolVersion protocolVersion ,
7685 String expectedClusterName ,
7786 EndPoint endPoint ,
7887 DriverChannelOptions options ,
79- HeartbeatHandler heartbeatHandler ) {
88+ HeartbeatHandler heartbeatHandler ,
89+ boolean querySupportedOptions ) {
8090
8191 this .context = context ;
8292 this .endPoint = endPoint ;
@@ -89,6 +99,7 @@ class ProtocolInitHandler extends ConnectInitHandler {
8999 this .expectedClusterName = expectedClusterName ;
90100 this .options = options ;
91101 this .heartbeatHandler = heartbeatHandler ;
102+ this .querySupportedOptions = querySupportedOptions ;
92103 this .logPrefix = options .ownerLogPrefix + "|connecting..." ;
93104 }
94105
@@ -117,6 +128,7 @@ protected boolean setConnectSuccess() {
117128 }
118129
119130 private enum Step {
131+ OPTIONS ,
120132 STARTUP ,
121133 GET_CLUSTER_NAME ,
122134 SET_KEYSPACE ,
@@ -133,7 +145,7 @@ private class InitRequest extends ChannelHandlerRequest {
133145
134146 InitRequest (ChannelHandlerContext ctx ) {
135147 super (ctx , timeoutMillis );
136- this .step = Step .STARTUP ;
148+ this .step = querySupportedOptions ? Step . OPTIONS : Step .STARTUP ;
137149 }
138150
139151 @ Override
@@ -144,6 +156,8 @@ String describe() {
144156 @ Override
145157 Message getRequest () {
146158 switch (step ) {
159+ case OPTIONS :
160+ return Options .INSTANCE ;
147161 case STARTUP :
148162 return new Startup (context .getStartupOptions ());
149163 case GET_CLUSTER_NAME :
@@ -167,7 +181,11 @@ void onResponse(Message response) {
167181 step ,
168182 ProtocolUtils .opcodeString (response .opcode ));
169183 try {
170- if (step == Step .STARTUP && response instanceof Ready ) {
184+ if (step == Step .OPTIONS && response instanceof Supported ) {
185+ channel .attr (DriverChannel .OPTIONS_KEY ).set (((Supported ) response ).options );
186+ step = Step .STARTUP ;
187+ send ();
188+ } else if (step == Step .STARTUP && response instanceof Ready ) {
171189 context .getAuthProvider ().ifPresent (provider -> provider .onMissingChallenge (endPoint ));
172190 step = Step .GET_CLUSTER_NAME ;
173191 send ();
@@ -265,12 +283,14 @@ void onResponse(Message response) {
265283 } else if (response instanceof Error ) {
266284 Error error = (Error ) response ;
267285 // Testing for a specific string is a tad fragile but Cassandra doesn't give us a more
268- // precise error
269- // code.
286+ // precise error code.
270287 // C* 2.1 reports a server error instead of protocol error, see CASSANDRA-9451.
271- if (step == Step .STARTUP
272- && (error .code == ProtocolConstants .ErrorCode .PROTOCOL_ERROR
273- || error .code == ProtocolConstants .ErrorCode .SERVER_ERROR )
288+ boolean firstRequest =
289+ (step == Step .OPTIONS && querySupportedOptions ) || step == Step .STARTUP ;
290+ boolean serverOrProtocolError =
291+ error .code == ErrorCode .PROTOCOL_ERROR || error .code == ErrorCode .SERVER_ERROR ;
292+ if (firstRequest
293+ && serverOrProtocolError
274294 && error .message .contains ("Invalid or unsupported protocol version" )) {
275295 fail (
276296 UnsupportedProtocolVersionException .forSingleAttempt (
0 commit comments