2019-05-22 21:23:23 +00:00
|
|
|
package handshake
|
|
|
|
|
|
|
|
import (
|
2024-07-05 08:40:46 +00:00
|
|
|
"encoding/json"
|
2023-05-18 17:37:56 +00:00
|
|
|
"errors"
|
2019-05-22 21:23:23 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net"
|
2024-07-05 08:40:46 +00:00
|
|
|
"sync"
|
2019-05-22 21:23:23 +00:00
|
|
|
"time"
|
2024-06-14 09:42:00 +00:00
|
|
|
"unsafe"
|
|
|
|
|
2024-07-05 08:40:46 +00:00
|
|
|
"github.com/valyala/fastjson"
|
|
|
|
|
2024-06-14 09:42:00 +00:00
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
2019-05-22 21:23:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-04-27 06:32:08 +00:00
|
|
|
vminsertHello = "vminsert.02"
|
2019-05-22 21:23:23 +00:00
|
|
|
vmselectHello = "vmselect.01"
|
|
|
|
|
|
|
|
successResponse = "ok"
|
|
|
|
)
|
|
|
|
|
2024-06-14 09:42:00 +00:00
|
|
|
// ClientFunc must perform handshake on the given c using the given compressionLevel.
|
|
|
|
//
|
|
|
|
// It must return BufferedConn wrapper for c on successful handshake.
|
|
|
|
type ClientFunc func(c net.Conn, compressionLevel int) (*BufferedConn, uint64, error)
|
|
|
|
|
|
|
|
// ServerFunc must perform handshake on the given c using the given compressionLevel and id.
|
2019-05-22 21:23:23 +00:00
|
|
|
//
|
|
|
|
// It must return BufferedConn wrapper for c on successful handshake.
|
2024-06-14 09:42:00 +00:00
|
|
|
type ServerFunc func(c net.Conn, compressionLevel int, id uint64) (*BufferedConn, error)
|
2019-05-22 21:23:23 +00:00
|
|
|
|
|
|
|
// VMInsertClient performs client-side handshake for vminsert protocol.
|
|
|
|
//
|
|
|
|
// compressionLevel is the level used for compression of the data sent
|
|
|
|
// to the server.
|
|
|
|
// compressionLevel <= 0 means 'no compression'
|
2024-06-14 09:42:00 +00:00
|
|
|
func VMInsertClient(c net.Conn, compressionLevel int) (*BufferedConn, uint64, error) {
|
2019-05-22 21:23:23 +00:00
|
|
|
return genericClient(c, vminsertHello, compressionLevel)
|
|
|
|
}
|
|
|
|
|
|
|
|
// VMInsertServer performs server-side handshake for vminsert protocol.
|
|
|
|
//
|
|
|
|
// compressionLevel is the level used for compression of the data sent
|
|
|
|
// to the client.
|
|
|
|
// compressionLevel <= 0 means 'no compression'
|
2024-06-14 09:42:00 +00:00
|
|
|
func VMInsertServer(c net.Conn, compressionLevel int, id uint64) (*BufferedConn, error) {
|
|
|
|
return genericServer(c, vminsertHello, compressionLevel, id)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// VMSelectClient performs client-side handshake for vmselect protocol.
|
|
|
|
//
|
|
|
|
// compressionLevel is the level used for compression of the data sent
|
|
|
|
// to the server.
|
|
|
|
// compressionLevel <= 0 means 'no compression'
|
2024-06-14 09:42:00 +00:00
|
|
|
func VMSelectClient(c net.Conn, compressionLevel int) (*BufferedConn, uint64, error) {
|
2019-05-22 21:23:23 +00:00
|
|
|
return genericClient(c, vmselectHello, compressionLevel)
|
|
|
|
}
|
|
|
|
|
|
|
|
// VMSelectServer performs server-side handshake for vmselect protocol.
|
|
|
|
//
|
|
|
|
// compressionLevel is the level used for compression of the data sent
|
|
|
|
// to the client.
|
|
|
|
// compressionLevel <= 0 means 'no compression'
|
2024-06-14 09:42:00 +00:00
|
|
|
func VMSelectServer(c net.Conn, compressionLevel int, id uint64) (*BufferedConn, error) {
|
|
|
|
return genericServer(c, vmselectHello, compressionLevel, id)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
|
2023-05-18 17:37:56 +00:00
|
|
|
// ErrIgnoreHealthcheck means the TCP healthckeck, which must be ignored.
|
|
|
|
//
|
|
|
|
// The TCP healthcheck is performed by opening and then immediately closing the connection.
|
|
|
|
var ErrIgnoreHealthcheck = fmt.Errorf("TCP healthcheck - ignore it")
|
|
|
|
|
2024-07-05 08:40:46 +00:00
|
|
|
type handshakeMetadata struct {
|
|
|
|
NodeID uint64 `json:"nodeId"`
|
|
|
|
}
|
|
|
|
|
2024-06-14 09:42:00 +00:00
|
|
|
func genericServer(c net.Conn, msg string, compressionLevel int, id uint64) (*BufferedConn, error) {
|
2019-05-22 21:23:23 +00:00
|
|
|
if err := readMessage(c, msg); err != nil {
|
2023-05-18 17:37:56 +00:00
|
|
|
if errors.Is(err, io.EOF) {
|
|
|
|
// This is TCP healthcheck, which must be ignored in order to prevent from logs pollution.
|
|
|
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1762
|
|
|
|
return nil, ErrIgnoreHealthcheck
|
|
|
|
}
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot read hello: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if err := writeMessage(c, successResponse); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot write success response on hello: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
isRemoteCompressed, err := readIsCompressed(c)
|
|
|
|
if err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot read isCompressed flag: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if err := writeMessage(c, successResponse); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot write success response on isCompressed: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if err := writeIsCompressed(c, compressionLevel > 0); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot write isCompressed flag: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if err := readMessage(c, successResponse); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot read success response on isCompressed: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
2024-07-05 08:40:46 +00:00
|
|
|
if err := writeMetadata(c, id); err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot write metadata: %w", err)
|
2024-06-14 09:42:00 +00:00
|
|
|
}
|
2019-05-22 21:23:23 +00:00
|
|
|
bc := newBufferedConn(c, compressionLevel, isRemoteCompressed)
|
|
|
|
return bc, nil
|
|
|
|
}
|
|
|
|
|
2024-06-14 09:42:00 +00:00
|
|
|
func genericClient(c net.Conn, msg string, compressionLevel int) (*BufferedConn, uint64, error) {
|
2019-05-22 21:23:23 +00:00
|
|
|
if err := writeMessage(c, msg); err != nil {
|
2024-06-14 09:42:00 +00:00
|
|
|
return nil, 0, fmt.Errorf("cannot write hello: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if err := readMessage(c, successResponse); err != nil {
|
2024-06-14 09:42:00 +00:00
|
|
|
return nil, 0, fmt.Errorf("cannot read success response after sending hello: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if err := writeIsCompressed(c, compressionLevel > 0); err != nil {
|
2024-06-14 09:42:00 +00:00
|
|
|
return nil, 0, fmt.Errorf("cannot write isCompressed flag: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if err := readMessage(c, successResponse); err != nil {
|
2024-06-14 09:42:00 +00:00
|
|
|
return nil, 0, fmt.Errorf("cannot read success response on isCompressed: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
isRemoteCompressed, err := readIsCompressed(c)
|
|
|
|
if err != nil {
|
2024-06-14 09:42:00 +00:00
|
|
|
return nil, 0, fmt.Errorf("cannot read isCompressed flag: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if err := writeMessage(c, successResponse); err != nil {
|
2024-06-14 09:42:00 +00:00
|
|
|
return nil, 0, fmt.Errorf("cannot write success response on isCompressed: %w", err)
|
|
|
|
}
|
2024-07-05 08:40:46 +00:00
|
|
|
metadata, err := readMetadata(c)
|
2024-06-14 09:42:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, 0, fmt.Errorf("cannot read nodeID: %w", err)
|
|
|
|
}
|
|
|
|
|
2019-05-22 21:23:23 +00:00
|
|
|
bc := newBufferedConn(c, compressionLevel, isRemoteCompressed)
|
2024-07-05 08:40:46 +00:00
|
|
|
return bc, metadata.NodeID, nil
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func writeIsCompressed(c net.Conn, isCompressed bool) error {
|
|
|
|
var buf [1]byte
|
|
|
|
if isCompressed {
|
|
|
|
buf[0] = 1
|
|
|
|
}
|
|
|
|
return writeMessage(c, string(buf[:]))
|
|
|
|
}
|
|
|
|
|
|
|
|
func readIsCompressed(c net.Conn) (bool, error) {
|
|
|
|
buf, err := readData(c, 1)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
isCompressed := (buf[0] != 0)
|
|
|
|
return isCompressed, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeMessage(c net.Conn, msg string) error {
|
|
|
|
if err := c.SetWriteDeadline(time.Now().Add(time.Second)); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return fmt.Errorf("cannot set write deadline: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if _, err := io.WriteString(c, msg); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return fmt.Errorf("cannot write %q to server: %w", msg, err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
if fc, ok := c.(flusher); ok {
|
|
|
|
if err := fc.Flush(); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return fmt.Errorf("cannot flush %q to server: %w", msg, err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
}
|
2022-02-07 12:36:41 +00:00
|
|
|
if err := c.SetWriteDeadline(time.Time{}); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return fmt.Errorf("cannot reset write deadline: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type flusher interface {
|
|
|
|
Flush() error
|
|
|
|
}
|
|
|
|
|
|
|
|
func readMessage(c net.Conn, msg string) error {
|
|
|
|
buf, err := readData(c, len(msg))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if string(buf) != msg {
|
|
|
|
return fmt.Errorf("unexpected message obtained; got %q; want %q", buf, msg)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-07-05 08:40:46 +00:00
|
|
|
var metadataParserPool fastjson.ParserPool
|
|
|
|
|
|
|
|
func readMetadata(c net.Conn) (*handshakeMetadata, error) {
|
|
|
|
metaLenBuf, err := readData(c, int(unsafe.Sizeof(uint64(0))))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot read metadata length: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
metaLen := int(encoding.UnmarshalUint64(metaLenBuf))
|
|
|
|
if err := writeMessage(c, successResponse); err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot write success response on metadata length: %w", err)
|
|
|
|
}
|
|
|
|
metaBuf, err := readData(c, metaLen)
|
2024-06-14 09:42:00 +00:00
|
|
|
if err != nil {
|
2024-07-05 08:40:46 +00:00
|
|
|
return nil, fmt.Errorf("cannot read metadata: %w", err)
|
|
|
|
}
|
|
|
|
if err := writeMessage(c, successResponse); err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot write success response on metadata: %w", err)
|
|
|
|
}
|
|
|
|
parser := metadataParserPool.Get()
|
|
|
|
defer metadataParserPool.Put(parser)
|
|
|
|
v, err := parser.ParseBytes(metaBuf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot parse metadata: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &handshakeMetadata{
|
|
|
|
NodeID: v.GetUint64("nodeId"),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
metadataCache sync.Map
|
|
|
|
)
|
|
|
|
|
|
|
|
func getMetadataBytes(id uint64) ([]byte, error) {
|
|
|
|
m, ok := metadataCache.Load(id)
|
|
|
|
if !ok {
|
|
|
|
metadata := handshakeMetadata{
|
|
|
|
NodeID: id,
|
|
|
|
}
|
|
|
|
var err error
|
|
|
|
m, err = json.Marshal(metadata)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot marshal metadata: %w", err)
|
|
|
|
}
|
|
|
|
metadataCache.Store(id, m)
|
2024-06-14 09:42:00 +00:00
|
|
|
}
|
2024-07-05 08:40:46 +00:00
|
|
|
metaV := m.([]byte)
|
|
|
|
return metaV, nil
|
2024-06-14 09:42:00 +00:00
|
|
|
}
|
|
|
|
|
2024-07-05 08:40:46 +00:00
|
|
|
func writeMetadata(c net.Conn, id uint64) error {
|
|
|
|
meta, err := getMetadataBytes(id)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot obtain metadata bytes: %w", err)
|
|
|
|
}
|
|
|
|
metaLen := len(meta)
|
|
|
|
if err := writeMessage(c, string(encoding.MarshalUint64(nil, uint64(metaLen)))); err != nil {
|
|
|
|
return fmt.Errorf("cannot write metadata length: %w", err)
|
|
|
|
}
|
|
|
|
if err := readMessage(c, successResponse); err != nil {
|
|
|
|
return fmt.Errorf("cannot read success response on metadata length: %w", err)
|
|
|
|
}
|
|
|
|
if err := writeMessage(c, string(meta[:])); err != nil {
|
|
|
|
return fmt.Errorf("cannot write metadata: %w", err)
|
|
|
|
}
|
|
|
|
if err := readMessage(c, successResponse); err != nil {
|
|
|
|
return fmt.Errorf("cannot read success response on metadata: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2024-06-14 09:42:00 +00:00
|
|
|
}
|
|
|
|
|
2019-05-22 21:23:23 +00:00
|
|
|
func readData(c net.Conn, dataLen int) ([]byte, error) {
|
|
|
|
if err := c.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot set read deadline: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
data := make([]byte, dataLen)
|
2019-09-11 11:11:37 +00:00
|
|
|
if n, err := io.ReadFull(c, data); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot read message with size %d: %w; read only %d bytes", dataLen, err, n)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
2022-02-07 12:36:41 +00:00
|
|
|
if err := c.SetReadDeadline(time.Time{}); err != nil {
|
2020-06-30 19:58:18 +00:00
|
|
|
return nil, fmt.Errorf("cannot reset read deadline: %w", err)
|
2019-05-22 21:23:23 +00:00
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|