gotidb/pkg/api/quic_server.go

211 lines
5.0 KiB
Go

package api
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"log"
"math/big"
"time"
"git.pyer.club/kingecg/gotidb/pkg/manager"
"git.pyer.club/kingecg/gotidb/pkg/model"
"github.com/quic-go/quic-go"
)
// QUICServer represents the QUIC protocol server
type QUICServer struct {
listener *quic.Listener
manager *manager.DataManager
tlsConfig *tls.Config
quicConfig *quic.Config
}
// QUICWriteRequest represents a write request from client
type QUICWriteRequest struct {
Points []DataPoint `json:"points"`
BatchID string `json:"batch_id"`
}
// DataPoint represents a single data point
type DataPoint struct {
DeviceID string `json:"device_id"`
MetricCode string `json:"metric_code"`
Labels map[string]string `json:"labels"`
Value float64 `json:"value"`
Timestamp int64 `json:"timestamp"`
}
// WriteResponse represents the response to a write request
type WriteResponse struct {
Success bool `json:"success"`
Error string `json:"error,omitempty"`
BatchID string `json:"batch_id"`
PointsCount int `json:"points_count"`
}
// NewQUICServer creates a new QUIC server instance
func NewQUICServer(dm *manager.DataManager, config *quic.Config) (*QUICServer, error) {
// Generate self-signed certificate for development
// In production, you should use proper certificates
tlsConfig, err := generateTLSConfig()
if err != nil {
return nil, fmt.Errorf("failed to generate TLS config: %v", err)
}
quicConfig := &quic.Config{
MaxIdleTimeout: 30 * time.Second,
KeepAlivePeriod: 10 * time.Second,
}
return &QUICServer{
manager: dm,
tlsConfig: tlsConfig,
quicConfig: quicConfig,
}, nil
}
// Start starts the QUIC server
func (s *QUICServer) Start(addr string) error {
listener, err := quic.ListenAddr(addr, s.tlsConfig, s.quicConfig)
if err != nil {
return fmt.Errorf("failed to create QUIC listener: %v", err)
}
s.listener = listener
go s.acceptConnections()
return nil
}
// Stop stops the QUIC server
func (s *QUICServer) Stop(ctx context.Context) error {
if s.listener != nil {
return s.listener.Close()
}
return nil
}
func (s *QUICServer) acceptConnections() {
for {
conn, err := s.listener.Accept(context.Background())
if err != nil {
log.Printf("Failed to accept QUIC connection: %v", err)
continue
}
go s.handleConnection(conn)
}
}
func (s *QUICServer) handleConnection(conn quic.Connection) {
for {
stream, err := conn.AcceptStream(context.Background())
if err != nil {
log.Printf("Failed to accept QUIC stream: %v", err)
return
}
go s.handleStream(stream)
}
}
func (s *QUICServer) handleStream(stream quic.Stream) {
defer stream.Close()
var req QUICWriteRequest
decoder := json.NewDecoder(stream)
if err := decoder.Decode(&req); err != nil {
s.sendError(stream, "Failed to decode request", err)
return
}
response := WriteResponse{
BatchID: req.BatchID,
PointsCount: len(req.Points),
}
// Process each data point
for _, point := range req.Points {
id := manager.CreateDataPoint(
point.DeviceID,
point.MetricCode,
point.Labels,
point.Value,
)
timestamp := time.Unix(point.Timestamp, 0)
value := model.DataValue{
Timestamp: timestamp,
Value: point.Value,
}
if err := s.manager.Write(context.Background(), id, value); err != nil {
s.sendError(stream, "Failed to write data point", err)
return
}
}
response.Success = true
encoder := json.NewEncoder(stream)
if err := encoder.Encode(response); err != nil {
log.Printf("Failed to send response: %v", err)
}
}
func (s *QUICServer) sendError(stream quic.Stream, msg string, err error) {
response := WriteResponse{
Success: false,
Error: fmt.Sprintf("%s: %v", msg, err),
}
encoder := json.NewEncoder(stream)
if err := encoder.Encode(response); err != nil {
log.Printf("Failed to send error response: %v", err)
}
}
// generateTLSConfig generates a self-signed certificate for development
func generateTLSConfig() (*tls.Config, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
template := x509.Certificate{
SerialNumber: big.NewInt(1),
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24 * 180), // Valid for 180 days
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
if err != nil {
return nil, err
}
keyPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
})
certPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certDER,
})
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return nil, err
}
return &tls.Config{
Certificates: []tls.Certificate{tlsCert},
NextProtos: []string{"gotidb-quic"}, // Application protocol
}, nil
}