snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
test / Lint (push) Failing after 3s
test / Test with Go 1.26.8 (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-16 01:17:21 +05:00
commit b352a9a30d
472 changed files with 108375 additions and 0 deletions

41
internal/unigrpc/codec.go Normal file
View File

@@ -0,0 +1,41 @@
package unigrpc
import (
"fmt"
"google.golang.org/protobuf/proto"
)
type rawFrame []byte
// RawCodec allows sending Protobuf encoded Pushes without
// additional wrapping and marshaling.
type RawCodec struct{}
func (c *RawCodec) Marshal(v any) ([]byte, error) {
out, ok := v.(rawFrame)
if !ok {
vv, ok := v.(proto.Message)
if !ok {
return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v)
}
return proto.Marshal(vv)
}
return out, nil
}
func (c *RawCodec) Unmarshal(data []byte, v any) error {
vv, ok := v.(proto.Message)
if !ok {
return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v)
}
return proto.Unmarshal(data, vv)
}
func (c *RawCodec) String() string {
return "proto"
}
func (c *RawCodec) Name() string {
return "proto"
}

View File

@@ -0,0 +1,5 @@
package unigrpc
import "github.com/centrifugal/centrifugo/v6/internal/configtypes"
type Config = configtypes.UniGRPC

84
internal/unigrpc/grpc.go Normal file
View File

@@ -0,0 +1,84 @@
package unigrpc
import (
"time"
"github.com/centrifugal/centrifugo/v6/internal/logging"
"github.com/centrifugal/centrifugo/v6/internal/unigrpc/unistream"
"github.com/centrifugal/centrifuge"
"github.com/centrifugal/protocol"
"github.com/rs/zerolog/log"
"google.golang.org/grpc"
)
// RegisterService ...
func RegisterService(server *grpc.Server, service unistream.CentrifugoUniStreamServer) error {
unistream.RegisterCentrifugoUniStreamServer(server, service)
return nil
}
// Service can work with client GRPC connections.
type Service struct {
unistream.UnimplementedCentrifugoUniStreamServer
config Config
node *centrifuge.Node
}
// NewService creates new Service.
func NewService(n *centrifuge.Node, c Config) *Service {
return &Service{
config: c,
node: n,
}
}
// Consume is a unidirectional server->client stream with real-time data.
func (s *Service) Consume(req *unistream.ConnectRequest, stream unistream.CentrifugoUniStream_ConsumeServer) error {
streamDataCh := make(chan rawFrame)
transport := newGRPCTransport(stream, streamDataCh)
connectRequest := &protocol.ConnectRequest{
Token: req.Token,
Data: req.Data,
Name: req.Name,
Version: req.Version,
Headers: req.Headers,
}
if req.Subs != nil {
subs := make(map[string]*protocol.SubscribeRequest, len(req.Subs))
for k, v := range req.Subs {
subs[k] = &protocol.SubscribeRequest{
Recover: v.Recover,
Offset: v.Offset,
Epoch: v.Epoch,
}
}
connectRequest.Subs = subs
}
c, closeFn, err := centrifuge.NewClient(stream.Context(), s.node, transport)
if err != nil {
return err
}
defer func() { _ = closeFn() }()
if logging.Enabled(logging.DebugLevel) {
log.Debug().Str("transport", transport.Name()).Str("client", c.ID()).Msg("client connection established")
defer func(started time.Time) {
log.Debug().Str("transport", transport.Name()).Str("client", c.ID()).
Str("duration", time.Since(started).String()).Msg("client connection completed")
}(time.Now())
}
c.ProtocolConnect(connectRequest)
for {
select {
case <-transport.closeCh:
return nil
case <-stream.Context().Done():
return nil
}
}
}

View File

@@ -0,0 +1,97 @@
package unigrpc
import (
"sync"
"github.com/centrifugal/centrifugo/v6/internal/unigrpc/unistream"
"github.com/centrifugal/centrifuge"
)
// grpcTransport wraps a stream.
type grpcTransport struct {
mu sync.RWMutex
stream unistream.CentrifugoUniStream_ConsumeServer
closed bool
closeCh chan struct{}
streamDataCh chan rawFrame
}
func newGRPCTransport(stream unistream.CentrifugoUniStream_ConsumeServer, streamDataCh chan rawFrame) *grpcTransport {
return &grpcTransport{
stream: stream,
streamDataCh: streamDataCh,
closeCh: make(chan struct{}),
}
}
const transportName = "uni_grpc"
func (t *grpcTransport) Name() string {
return transportName
}
func (t *grpcTransport) AcceptProtocol() string {
return "h2" // Always for GRPC for now.
}
func (t *grpcTransport) Protocol() centrifuge.ProtocolType {
return centrifuge.ProtocolTypeProtobuf
}
// ProtocolVersion returns transport protocol version.
func (t *grpcTransport) ProtocolVersion() centrifuge.ProtocolVersion {
return centrifuge.ProtocolVersion2
}
// Unidirectional returns whether transport is unidirectional.
func (t *grpcTransport) Unidirectional() bool {
return true
}
// DisabledPushFlags ...
func (t *grpcTransport) DisabledPushFlags() uint64 {
return 0
}
// PingPongConfig ...
func (t *grpcTransport) PingPongConfig() centrifuge.PingPongConfig {
return centrifuge.PingPongConfig{
PingInterval: 0,
}
}
// Emulation ...
func (t *grpcTransport) Emulation() bool {
return false
}
func (t *grpcTransport) Write(message []byte) error {
return t.WriteMany(message)
}
func (t *grpcTransport) WriteMany(messages ...[]byte) error {
t.mu.RLock()
if t.closed {
t.mu.RUnlock()
return nil
}
t.mu.RUnlock()
for i := 0; i < len(messages); i++ {
err := t.stream.SendMsg(rawFrame(messages[i]))
if err != nil {
return err
}
}
return nil
}
func (t *grpcTransport) Close(_ centrifuge.Disconnect) error {
t.mu.Lock()
defer t.mu.Unlock()
if t.closed {
return nil
}
close(t.closeCh)
return nil
}

View File

@@ -0,0 +1,11 @@
#!/bin/bash
set -e
# go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
# go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
protoc -I ./ \
unistream.proto \
--go_out=. \
--go-grpc_out=.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,127 @@
syntax = "proto3";
package centrifugal.centrifugo.unistream;
option go_package = "./;unistream";
service CentrifugoUniStream {
rpc Consume(ConnectRequest) returns (stream Push);
}
message ConnectRequest {
// Numeration is in sync with Centrifuge client protocol.
string token = 1;
bytes data = 2;
map<string, SubscribeRequest> subs = 3;
string name = 4;
string version = 5;
map<string, string> headers = 6;
}
message SubscribeRequest {
// Numeration is in sync with Centrifuge client protocol.
bool recover = 3;
reserved 4, 5;
string epoch = 6;
uint64 offset = 7;
}
// Push can be sent to a client as part of Reply in case of bidirectional transport or
// without additional wrapping in case of unidirectional transports.
// ProtocolVersion2 uses channel and one of the possible concrete push messages.
message Push {
reserved 1, 3;
string channel = 2;
// ProtocolVersion2 server can push one of the following fields to the client. We are
// not using oneof here due to JSON interoperability concerns.
Publication pub = 4;
Join join = 5;
Leave leave = 6;
Unsubscribe unsubscribe = 7;
Message message = 8;
Subscribe subscribe = 9;
Connect connect = 10;
Disconnect disconnect = 11;
Refresh refresh = 12;
}
message ClientInfo {
string user = 1;
string client = 2;
bytes conn_info = 3;
bytes chan_info = 4;
}
message Publication {
reserved 1, 2, 3;
bytes data = 4;
ClientInfo info = 5;
uint64 offset = 6;
map<string, string> tags = 7;
}
message Join {
ClientInfo info = 1;
}
message Leave {
ClientInfo info = 1;
}
message Unsubscribe {
reserved 1;
uint32 code = 2;
string reason = 3;
}
message Subscribe {
bool recoverable = 1;
reserved 2, 3;
string epoch = 4;
uint64 offset = 5;
bool positioned = 6;
bytes data = 7;
}
message Message {
bytes data = 1;
}
message Connect {
string client = 1;
string version = 2;
bytes data = 3;
map<string, SubscribeResult> subs = 4;
bool expires = 5;
uint32 ttl = 6;
uint32 ping = 7;
bool pong = 8;
string session = 9;
string node = 10;
}
message Disconnect {
uint32 code = 1;
string reason = 2;
bool reconnect = 3;
}
message Refresh {
bool expires = 1;
uint32 ttl = 2;
}
message SubscribeResult {
bool expires = 1;
uint32 ttl = 2;
bool recoverable = 3;
reserved 4, 5;
string epoch = 6;
repeated Publication publications = 7;
bool recovered = 8;
uint64 offset = 9;
bool positioned = 10;
bytes data = 11;
bool was_recovering = 12;
}

View File

@@ -0,0 +1,124 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v7.34.0
// source: unistream.proto
package unistream
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
CentrifugoUniStream_Consume_FullMethodName = "/centrifugal.centrifugo.unistream.CentrifugoUniStream/Consume"
)
// CentrifugoUniStreamClient is the client API for CentrifugoUniStream service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type CentrifugoUniStreamClient interface {
Consume(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Push], error)
}
type centrifugoUniStreamClient struct {
cc grpc.ClientConnInterface
}
func NewCentrifugoUniStreamClient(cc grpc.ClientConnInterface) CentrifugoUniStreamClient {
return &centrifugoUniStreamClient{cc}
}
func (c *centrifugoUniStreamClient) Consume(ctx context.Context, in *ConnectRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Push], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &CentrifugoUniStream_ServiceDesc.Streams[0], CentrifugoUniStream_Consume_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[ConnectRequest, Push]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type CentrifugoUniStream_ConsumeClient = grpc.ServerStreamingClient[Push]
// CentrifugoUniStreamServer is the server API for CentrifugoUniStream service.
// All implementations must embed UnimplementedCentrifugoUniStreamServer
// for forward compatibility.
type CentrifugoUniStreamServer interface {
Consume(*ConnectRequest, grpc.ServerStreamingServer[Push]) error
mustEmbedUnimplementedCentrifugoUniStreamServer()
}
// UnimplementedCentrifugoUniStreamServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedCentrifugoUniStreamServer struct{}
func (UnimplementedCentrifugoUniStreamServer) Consume(*ConnectRequest, grpc.ServerStreamingServer[Push]) error {
return status.Error(codes.Unimplemented, "method Consume not implemented")
}
func (UnimplementedCentrifugoUniStreamServer) mustEmbedUnimplementedCentrifugoUniStreamServer() {}
func (UnimplementedCentrifugoUniStreamServer) testEmbeddedByValue() {}
// UnsafeCentrifugoUniStreamServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to CentrifugoUniStreamServer will
// result in compilation errors.
type UnsafeCentrifugoUniStreamServer interface {
mustEmbedUnimplementedCentrifugoUniStreamServer()
}
func RegisterCentrifugoUniStreamServer(s grpc.ServiceRegistrar, srv CentrifugoUniStreamServer) {
// If the following call panics, it indicates UnimplementedCentrifugoUniStreamServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&CentrifugoUniStream_ServiceDesc, srv)
}
func _CentrifugoUniStream_Consume_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ConnectRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(CentrifugoUniStreamServer).Consume(m, &grpc.GenericServerStream[ConnectRequest, Push]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type CentrifugoUniStream_ConsumeServer = grpc.ServerStreamingServer[Push]
// CentrifugoUniStream_ServiceDesc is the grpc.ServiceDesc for CentrifugoUniStream service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var CentrifugoUniStream_ServiceDesc = grpc.ServiceDesc{
ServiceName: "centrifugal.centrifugo.unistream.CentrifugoUniStream",
HandlerType: (*CentrifugoUniStreamServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "Consume",
Handler: _CentrifugoUniStream_Consume_Handler,
ServerStreams: true,
},
},
Metadata: "unistream.proto",
}