mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-05-22 14:56:34 +00:00

Adds new suite of metrics to capture the following for capnp rpcs operations: - Method calls - Method call failures - Method call latencies Each of the operations is labeled by the handler that serves the method and the method of operation invoked. Additionally, each of these are split between if the operation was called by a client or served.
313 lines
8.5 KiB
Go
313 lines
8.5 KiB
Go
package pogs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
capnp "zombiezen.com/go/capnproto2"
|
|
"zombiezen.com/go/capnproto2/pogs"
|
|
"zombiezen.com/go/capnproto2/rpc"
|
|
"zombiezen.com/go/capnproto2/server"
|
|
|
|
"github.com/cloudflare/cloudflared/tunnelrpc/metrics"
|
|
"github.com/cloudflare/cloudflared/tunnelrpc/proto"
|
|
)
|
|
|
|
type RegistrationServer interface {
|
|
// RegisterConnection is the call typically handled by the edge to initiate and authenticate a new connection
|
|
// for cloudflared.
|
|
RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error)
|
|
// UnregisterConnection is the call typically handled by the edge to close an existing connection for cloudflared.
|
|
UnregisterConnection(ctx context.Context)
|
|
// UpdateLocalConfiguration is the call typically handled by the edge for cloudflared to provide the current
|
|
// configuration it is operating with.
|
|
UpdateLocalConfiguration(ctx context.Context, config []byte) error
|
|
}
|
|
|
|
type RegistrationServer_PogsImpl struct {
|
|
impl RegistrationServer
|
|
}
|
|
|
|
func RegistrationServer_ServerToClient(s RegistrationServer) proto.RegistrationServer {
|
|
return proto.RegistrationServer_ServerToClient(RegistrationServer_PogsImpl{s})
|
|
}
|
|
|
|
func (i RegistrationServer_PogsImpl) RegisterConnection(p proto.RegistrationServer_registerConnection) error {
|
|
return metrics.ObserveServerHandler(func() error { return i.registerConnection(p) }, metrics.Registration, metrics.OperationRegisterConnection)
|
|
}
|
|
|
|
func (i RegistrationServer_PogsImpl) registerConnection(p proto.RegistrationServer_registerConnection) error {
|
|
server.Ack(p.Options)
|
|
|
|
auth, err := p.Params.Auth()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var pogsAuth TunnelAuth
|
|
err = pogsAuth.UnmarshalCapnproto(auth)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
uuidBytes, err := p.Params.TunnelId()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tunnelID, err := uuid.FromBytes(uuidBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
connIndex := p.Params.ConnIndex()
|
|
options, err := p.Params.Options()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var pogsOptions ConnectionOptions
|
|
err = pogsOptions.UnmarshalCapnproto(options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
connDetails, callError := i.impl.RegisterConnection(p.Ctx, pogsAuth, tunnelID, connIndex, &pogsOptions)
|
|
|
|
resp, err := p.Results.NewResult()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if callError != nil {
|
|
if connError, err := resp.Result().NewError(); err != nil {
|
|
return err
|
|
} else {
|
|
return MarshalError(connError, callError)
|
|
}
|
|
}
|
|
|
|
if details, err := resp.Result().NewConnectionDetails(); err != nil {
|
|
return err
|
|
} else {
|
|
return connDetails.MarshalCapnproto(details)
|
|
}
|
|
}
|
|
|
|
func (i RegistrationServer_PogsImpl) UnregisterConnection(p proto.RegistrationServer_unregisterConnection) error {
|
|
return metrics.ObserveServerHandler(func() error {
|
|
server.Ack(p.Options)
|
|
i.impl.UnregisterConnection(p.Ctx)
|
|
return nil // No metrics will be reported for failure as this method has no return value
|
|
}, metrics.Registration, metrics.OperationUnregisterConnection)
|
|
}
|
|
|
|
func (i RegistrationServer_PogsImpl) UpdateLocalConfiguration(p proto.RegistrationServer_updateLocalConfiguration) error {
|
|
return metrics.ObserveServerHandler(func() error { return i.updateLocalConfiguration(p) }, metrics.Registration, metrics.OperationUpdateLocalConfiguration)
|
|
}
|
|
|
|
func (i RegistrationServer_PogsImpl) updateLocalConfiguration(c proto.RegistrationServer_updateLocalConfiguration) error {
|
|
server.Ack(c.Options)
|
|
|
|
configBytes, err := c.Params.Config()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return i.impl.UpdateLocalConfiguration(c.Ctx, configBytes)
|
|
}
|
|
|
|
type RegistrationServer_PogsClient struct {
|
|
Client capnp.Client
|
|
Conn *rpc.Conn
|
|
}
|
|
|
|
func NewRegistrationServer_PogsClient(client capnp.Client, conn *rpc.Conn) RegistrationServer_PogsClient {
|
|
return RegistrationServer_PogsClient{
|
|
Client: client,
|
|
Conn: conn,
|
|
}
|
|
}
|
|
|
|
func (c RegistrationServer_PogsClient) Close() error {
|
|
c.Client.Close()
|
|
return c.Conn.Close()
|
|
}
|
|
|
|
func (c RegistrationServer_PogsClient) RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) {
|
|
client := proto.TunnelServer{Client: c.Client}
|
|
promise := client.RegisterConnection(ctx, func(p proto.RegistrationServer_registerConnection_Params) error {
|
|
tunnelAuth, err := p.NewAuth()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = auth.MarshalCapnproto(tunnelAuth); err != nil {
|
|
return err
|
|
}
|
|
err = p.SetAuth(tunnelAuth)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = p.SetTunnelId(tunnelID[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.SetConnIndex(connIndex)
|
|
connectionOptions, err := p.NewOptions()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = options.MarshalCapnproto(connectionOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
response, err := promise.Result().Struct()
|
|
if err != nil {
|
|
return nil, wrapRPCError(err)
|
|
}
|
|
result := response.Result()
|
|
switch result.Which() {
|
|
case proto.ConnectionResponse_result_Which_error:
|
|
resultError, err := result.Error()
|
|
if err != nil {
|
|
return nil, wrapRPCError(err)
|
|
}
|
|
cause, err := resultError.Cause()
|
|
if err != nil {
|
|
return nil, wrapRPCError(err)
|
|
}
|
|
err = errors.New(cause)
|
|
if resultError.ShouldRetry() {
|
|
err = RetryErrorAfter(err, time.Duration(resultError.RetryAfter()))
|
|
}
|
|
return nil, err
|
|
|
|
case proto.ConnectionResponse_result_Which_connectionDetails:
|
|
connDetails, err := result.ConnectionDetails()
|
|
if err != nil {
|
|
return nil, wrapRPCError(err)
|
|
}
|
|
details := new(ConnectionDetails)
|
|
if err = details.UnmarshalCapnproto(connDetails); err != nil {
|
|
return nil, wrapRPCError(err)
|
|
}
|
|
return details, nil
|
|
}
|
|
|
|
return nil, newRPCError("unknown result which %d", result.Which())
|
|
}
|
|
|
|
func (c RegistrationServer_PogsClient) SendLocalConfiguration(ctx context.Context, config []byte) error {
|
|
client := proto.TunnelServer{Client: c.Client}
|
|
promise := client.UpdateLocalConfiguration(ctx, func(p proto.RegistrationServer_updateLocalConfiguration_Params) error {
|
|
if err := p.SetConfig(config); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
_, err := promise.Struct()
|
|
if err != nil {
|
|
return wrapRPCError(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c RegistrationServer_PogsClient) UnregisterConnection(ctx context.Context) error {
|
|
client := proto.TunnelServer{Client: c.Client}
|
|
promise := client.UnregisterConnection(ctx, func(p proto.RegistrationServer_unregisterConnection_Params) error {
|
|
return nil
|
|
})
|
|
_, err := promise.Struct()
|
|
if err != nil {
|
|
return wrapRPCError(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type ClientInfo struct {
|
|
ClientID []byte `capnp:"clientId"` // must be a slice for capnp compatibility
|
|
Features []string
|
|
Version string
|
|
Arch string
|
|
}
|
|
|
|
type ConnectionOptions struct {
|
|
Client ClientInfo
|
|
OriginLocalIP net.IP `capnp:"originLocalIp"`
|
|
ReplaceExisting bool
|
|
CompressionQuality uint8
|
|
NumPreviousAttempts uint8
|
|
}
|
|
|
|
type TunnelAuth struct {
|
|
AccountTag string
|
|
TunnelSecret []byte
|
|
}
|
|
|
|
func (p *ConnectionOptions) MarshalCapnproto(s proto.ConnectionOptions) error {
|
|
return pogs.Insert(proto.ConnectionOptions_TypeID, s.Struct, p)
|
|
}
|
|
|
|
func (p *ConnectionOptions) UnmarshalCapnproto(s proto.ConnectionOptions) error {
|
|
return pogs.Extract(p, proto.ConnectionOptions_TypeID, s.Struct)
|
|
}
|
|
|
|
func (a *TunnelAuth) MarshalCapnproto(s proto.TunnelAuth) error {
|
|
return pogs.Insert(proto.TunnelAuth_TypeID, s.Struct, a)
|
|
}
|
|
|
|
func (a *TunnelAuth) UnmarshalCapnproto(s proto.TunnelAuth) error {
|
|
return pogs.Extract(a, proto.TunnelAuth_TypeID, s.Struct)
|
|
}
|
|
|
|
type ConnectionDetails struct {
|
|
UUID uuid.UUID
|
|
Location string
|
|
TunnelIsRemotelyManaged bool
|
|
}
|
|
|
|
func (details *ConnectionDetails) MarshalCapnproto(s proto.ConnectionDetails) error {
|
|
if err := s.SetUuid(details.UUID[:]); err != nil {
|
|
return err
|
|
}
|
|
if err := s.SetLocationName(details.Location); err != nil {
|
|
return err
|
|
}
|
|
s.SetTunnelIsRemotelyManaged(details.TunnelIsRemotelyManaged)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (details *ConnectionDetails) UnmarshalCapnproto(s proto.ConnectionDetails) error {
|
|
uuidBytes, err := s.Uuid()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
details.UUID, err = uuid.FromBytes(uuidBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
details.Location, err = s.LocationName()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
details.TunnelIsRemotelyManaged = s.TunnelIsRemotelyManaged()
|
|
|
|
return err
|
|
}
|
|
|
|
func MarshalError(s proto.ConnectionError, err error) error {
|
|
if err := s.SetCause(err.Error()); err != nil {
|
|
return err
|
|
}
|
|
if retryableErr, ok := err.(*RetryableError); ok {
|
|
s.SetShouldRetry(true)
|
|
s.SetRetryAfter(int64(retryableErr.Delay))
|
|
}
|
|
|
|
return nil
|
|
}
|