snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
120
internal/app/grpc.go
Normal file
120
internal/app/grpc.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/centrifugal/centrifugo/v6/internal/api"
|
||||
"github.com/centrifugal/centrifugo/v6/internal/config"
|
||||
"github.com/centrifugal/centrifugo/v6/internal/tools"
|
||||
"github.com/centrifugal/centrifugo/v6/internal/unigrpc"
|
||||
|
||||
"github.com/centrifugal/centrifuge"
|
||||
"github.com/rs/zerolog/log"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
func runGRPCAPIServer(cfg config.Config, node *centrifuge.Node, useAPIOpentelemetry bool, grpcAPIExecutor *api.Executor) (*grpc.Server, error) {
|
||||
grpcAPIAddr := net.JoinHostPort(cfg.GrpcAPI.Address, strconv.Itoa(cfg.GrpcAPI.Port))
|
||||
grpcAPIConn, err := net.Listen("tcp", grpcAPIAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot listen to address %s", grpcAPIAddr)
|
||||
}
|
||||
var grpcOpts []grpc.ServerOption
|
||||
|
||||
// NOTE: not a bug – GRPC API without a key is intentional and documented behaviour in
|
||||
// Centrifugo v6, see https://centrifugal.dev/docs/server/server_api. Unlike the HTTP API,
|
||||
// GRPC API is disabled by default and runs on its own port, separate from the main HTTP
|
||||
// server, so it's expected to be exposed to an internal network only. It may also be
|
||||
// protected without a key – by mutual TLS via grpc_api.tls, or JWKS-based auth in PRO.
|
||||
// Do not make an empty key fail-closed here: it silently breaks existing setups. In v7 we
|
||||
// may require at least one auth method unless an explicit insecure flag is set.
|
||||
if cfg.GrpcAPI.Key != "" {
|
||||
grpcOpts = append(grpcOpts, api.GRPCKeyAuth(cfg.GrpcAPI.Key))
|
||||
}
|
||||
if cfg.GrpcAPI.MaxReceiveMessageSize > 0 {
|
||||
grpcOpts = append(grpcOpts, grpc.MaxRecvMsgSize(cfg.GrpcAPI.MaxReceiveMessageSize))
|
||||
}
|
||||
var grpcAPITLSConfig *tls.Config
|
||||
if cfg.GrpcAPI.TLS.Enabled {
|
||||
grpcAPITLSConfig, err = cfg.GrpcAPI.TLS.ToGoTLSConfig("grpc_api")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting TLS config for GRPC API: %v", err)
|
||||
}
|
||||
}
|
||||
if grpcAPITLSConfig != nil {
|
||||
grpcOpts = append(grpcOpts, grpc.Creds(credentials.NewTLS(grpcAPITLSConfig)))
|
||||
}
|
||||
if cfg.OpenTelemetry.Enabled && cfg.OpenTelemetry.API {
|
||||
grpcOpts = append(grpcOpts, grpc.StatsHandler(otelgrpc.NewServerHandler()))
|
||||
}
|
||||
grpcErrorMode, err := tools.OptionalStringChoice(cfg.GrpcAPI.ErrorMode, []string{config.TransportErrorMode})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't extract grpc error mode: %v", err)
|
||||
}
|
||||
grpcAPIServer := grpc.NewServer(grpcOpts...)
|
||||
_ = api.RegisterGRPCServerAPI(node, grpcAPIExecutor, grpcAPIServer, api.GRPCAPIServiceConfig{
|
||||
UseOpenTelemetry: useAPIOpentelemetry,
|
||||
UseTransportErrorMode: grpcErrorMode == config.TransportErrorMode,
|
||||
})
|
||||
if cfg.GrpcAPI.Reflection {
|
||||
reflection.Register(grpcAPIServer)
|
||||
}
|
||||
log.Info().Msgf("serving GRPC API service on %s", grpcAPIAddr)
|
||||
go func() {
|
||||
if err := grpcAPIServer.Serve(grpcAPIConn); err != nil {
|
||||
log.Fatal().Err(err).Msg("serve GRPC API")
|
||||
}
|
||||
}()
|
||||
return grpcAPIServer, nil
|
||||
}
|
||||
|
||||
func runGRPCUniServer(cfg config.Config, node *centrifuge.Node) (*grpc.Server, error) {
|
||||
grpcUniAddr := net.JoinHostPort(cfg.UniGRPC.Address, strconv.Itoa(cfg.UniGRPC.Port))
|
||||
grpcUniConn, err := net.Listen("tcp", grpcUniAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot listen to address %s", grpcUniAddr)
|
||||
}
|
||||
var grpcOpts []grpc.ServerOption
|
||||
grpcOpts = append(grpcOpts, grpc.ForceServerCodec(&unigrpc.RawCodec{}))
|
||||
|
||||
if cfg.UniGRPC.MaxReceiveMessageSize > 0 {
|
||||
grpcOpts = append(grpcOpts, grpc.MaxRecvMsgSize(cfg.UniGRPC.MaxReceiveMessageSize))
|
||||
}
|
||||
|
||||
var uniGrpcTLSConfig *tls.Config
|
||||
if cfg.UniGRPC.TLS.Enabled {
|
||||
uniGrpcTLSConfig, err = cfg.UniGRPC.TLS.ToGoTLSConfig("uni_grpc")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting TLS config for uni GRPC: %v", err)
|
||||
}
|
||||
}
|
||||
if uniGrpcTLSConfig != nil {
|
||||
grpcOpts = append(grpcOpts, grpc.Creds(credentials.NewTLS(uniGrpcTLSConfig)))
|
||||
}
|
||||
keepAliveEnforcementPolicy := keepalive.EnforcementPolicy{
|
||||
MinTime: 5 * time.Second,
|
||||
}
|
||||
keepAliveServerParams := keepalive.ServerParameters{
|
||||
Time: 25 * time.Second,
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
grpcOpts = append(grpcOpts, grpc.KeepaliveEnforcementPolicy(keepAliveEnforcementPolicy))
|
||||
grpcOpts = append(grpcOpts, grpc.KeepaliveParams(keepAliveServerParams))
|
||||
grpcUniServer := grpc.NewServer(grpcOpts...)
|
||||
_ = unigrpc.RegisterService(grpcUniServer, unigrpc.NewService(node, cfg.UniGRPC))
|
||||
log.Info().Msgf("serving unidirectional GRPC on %s", grpcUniAddr)
|
||||
go func() {
|
||||
if err := grpcUniServer.Serve(grpcUniConn); err != nil {
|
||||
log.Fatal().Err(err).Msg("serve uni GRPC")
|
||||
}
|
||||
}()
|
||||
return grpcUniServer, nil
|
||||
}
|
||||
Reference in New Issue
Block a user