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  }