github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/client/handlers.go (about) 1 // Copyright 2020 DataStax 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package client 16 17 import ( 18 "strings" 19 20 "github.com/rs/zerolog/log" 21 22 "github.com/datastax/go-cassandra-native-protocol/frame" 23 "github.com/datastax/go-cassandra-native-protocol/message" 24 ) 25 26 // A RequestHandler to handle server-side heartbeats. This handler assumes that every OPTIONS request is a heartbeat 27 // probe and replies with a SUPPORTED response. 28 var HeartbeatHandler RequestHandler = func(request *frame.Frame, conn *CqlServerConnection, _ RequestHandlerContext) (response *frame.Frame) { 29 if _, ok := request.Body.Message.(*message.Options); ok { 30 log.Debug().Msgf("%v: [heartbeat handler]: received heartbeat probe", conn) 31 response = frame.NewFrame(request.Header.Version, request.Header.StreamId, &message.Supported{}) 32 } 33 return 34 } 35 36 // A RequestHandler to handle USE queries. This handler intercepts QUERY requests with a USE statement and replies 37 // with a message.SetKeyspaceResult. The provided callback function will be invoked with the new keyspace. 38 func NewSetKeyspaceHandler(onKeyspaceSet func(string)) RequestHandler { 39 return func(request *frame.Frame, conn *CqlServerConnection, _ RequestHandlerContext) (response *frame.Frame) { 40 if query, ok := request.Body.Message.(*message.Query); ok { 41 q := strings.TrimSpace(strings.ToLower(query.Query)) 42 q = strings.Join(strings.Fields(q), " ") 43 if strings.HasPrefix(q, "use ") { 44 keyspace := strings.TrimPrefix(q, "use ") 45 onKeyspaceSet(keyspace) 46 log.Debug().Msgf("%v: [set keyspace handler]: received USE %v", conn, keyspace) 47 response = frame.NewFrame(request.Header.Version, request.Header.StreamId, &message.SetKeyspaceResult{Keyspace: keyspace}) 48 } 49 } 50 return 51 } 52 } 53 54 // A RequestHandler to handle USE requests. This handler intercepts REGISTER requests and replies with READY. 55 var RegisterHandler RequestHandler = func(request *frame.Frame, conn *CqlServerConnection, _ RequestHandlerContext) (response *frame.Frame) { 56 if register, ok := request.Body.Message.(*message.Register); ok { 57 log.Debug().Msgf("%v: [register handler]: received REGISTER: %v", conn, register.EventTypes) 58 response = frame.NewFrame(request.Header.Version, request.Header.StreamId, &message.Ready{}) 59 } 60 return 61 } 62 63 // Creates a new composite RequestHandler combining many child handlers together. The child handlers are invoked in 64 // order. Registering a composite handler is functionally equivalent to the individual registration of its child 65 // handlers, but allows to ensure that handlers that are supposed to work together are all registered and invoked in 66 // proper order. 67 func NewCompositeRequestHandler(handlers ...RequestHandler) RequestHandler { 68 return func(request *frame.Frame, conn *CqlServerConnection, ctx RequestHandlerContext) (response *frame.Frame) { 69 for _, handler := range handlers { 70 response = handler(request, conn, ctx) 71 if response != nil { 72 break 73 } 74 } 75 return 76 } 77 } 78 79 // A RequestHandler to fully initialize a connection initiated by a DataStax driver. This handler intercepts all the 80 // requests that a driver typically issues when opening a new connection and / or probing for its liveness: 81 // - Heartbeats 82 // - Handshake, including with plain-text authentication if configured 83 // - USE queries 84 // - REGISTER requests 85 // - Queries targeting system.local and system.peers tables 86 func NewDriverConnectionInitializationHandler(cluster string, datacenter string, onKeyspaceSet func(string)) RequestHandler { 87 return NewCompositeRequestHandler( 88 HeartbeatHandler, 89 HandshakeHandler, 90 NewSetKeyspaceHandler(onKeyspaceSet), 91 RegisterHandler, 92 NewSystemTablesHandler(cluster, datacenter), 93 ) 94 }