Skip to content

Commit eb2e434

Browse files
committed
TUN-8415: Refactor capnp rpc into a single module
Combines the tunnelrpc and quic/schema capnp files into the same module. To help reduce future issues with capnp id generation, capnpids are provided in the capnp files from the existing capnp struct ids generated in the go files. Reduces the overall interface of the Capnp methods to the rest of the code by providing an interface that will handle the quic protocol selection. Introduces a new `rpc-timeout` config that will allow all of the SessionManager and ConfigurationManager RPC requests to have a timeout. The timeout for these values is set to 5 seconds as non of these operations for the managers should take a long time to complete. Removed the RPC-specific logger as it never provided good debugging value as the RPC method names were not visible in the logs.
1 parent 7d76ce2 commit eb2e434

39 files changed

+1121
-1028
lines changed

Makefile

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -251,22 +251,16 @@ github-windows-upload:
251251
python3 github_release.py --path built_artifacts/cloudflared-windows-386.exe --release-version $(VERSION) --name cloudflared-windows-386.exe
252252
python3 github_release.py --path built_artifacts/cloudflared-windows-386.msi --release-version $(VERSION) --name cloudflared-windows-386.msi
253253

254-
.PHONY: tunnelrpc-deps
255-
tunnelrpc-deps:
254+
.PHONY: capnp
255+
capnp:
256256
which capnp # https://capnproto.org/install.html
257257
which capnpc-go # go install zombiezen.com/go/capnproto2/capnpc-go@latest
258-
capnp compile -ogo tunnelrpc/tunnelrpc.capnp
259-
260-
.PHONY: quic-deps
261-
quic-deps:
262-
which capnp
263-
which capnpc-go
264-
capnp compile -ogo quic/schema/quic_metadata_protocol.capnp
258+
capnp compile -ogo tunnelrpc/proto/tunnelrpc.capnp tunnelrpc/proto/quic_metadata_protocol.capnp
265259

266260
.PHONY: vet
267261
vet:
268262
go vet -mod=vendor github.com/cloudflare/cloudflared/...
269263

270264
.PHONY: fmt
271265
fmt:
272-
goimports -l -w -local github.com/cloudflare/cloudflared $$(go list -mod=vendor -f '{{.Dir}}' -a ./... | fgrep -v tunnelrpc)
266+
goimports -l -w -local github.com/cloudflare/cloudflared $$(go list -mod=vendor -f '{{.Dir}}' -a ./... | fgrep -v tunnelrpc/proto)

cmd/cloudflared/tunnel/cmd.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ const (
7878
// hostKeyPath is the path of the dir to save SSH host keys too
7979
hostKeyPath = "host-key-path"
8080

81-
// udpUnregisterSessionTimeout is how long we wait before we stop trying to unregister a UDP session from the edge
82-
udpUnregisterSessionTimeoutFlag = "udp-unregister-session-timeout"
81+
// rpcTimeout is how long to wait for a Capnp RPC request to the edge
82+
rpcTimeout = "rpc-timeout"
8383

8484
// writeStreamTimeout sets if we should have a timeout when writing data to a stream towards the destination (edge/origin).
8585
writeStreamTimeout = "write-stream-timeout"
@@ -695,7 +695,7 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
695695
Hidden: true,
696696
}),
697697
altsrc.NewDurationFlag(&cli.DurationFlag{
698-
Name: udpUnregisterSessionTimeoutFlag,
698+
Name: rpcTimeout,
699699
Value: 5 * time.Second,
700700
Hidden: true,
701701
}),

cmd/cloudflared/tunnel/configuration.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func prepareTunnelConfig(
246246
EdgeTLSConfigs: edgeTLSConfigs,
247247
FeatureSelector: featureSelector,
248248
MaxEdgeAddrRetries: uint8(c.Int("max-edge-addr-retries")),
249-
UDPUnregisterSessionTimeout: c.Duration(udpUnregisterSessionTimeoutFlag),
249+
RPCTimeout: c.Duration(rpcTimeout),
250250
WriteStreamTimeout: c.Duration(writeStreamTimeout),
251251
DisableQUICPathMTUDiscovery: c.Bool(quicDisablePathMTUDiscovery),
252252
}

connection/quic.go

Lines changed: 34 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ import (
2828
"github.com/cloudflare/cloudflared/ingress"
2929
"github.com/cloudflare/cloudflared/management"
3030
"github.com/cloudflare/cloudflared/packet"
31-
quicpogs "github.com/cloudflare/cloudflared/quic"
31+
cfdquic "github.com/cloudflare/cloudflared/quic"
3232
"github.com/cloudflare/cloudflared/tracing"
33+
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
3334
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
35+
rpcquic "github.com/cloudflare/cloudflared/tunnelrpc/quic"
3436
)
3537

3638
const (
@@ -59,14 +61,14 @@ type QUICConnection struct {
5961
// sessionManager tracks active sessions. It receives datagrams from quic connection via datagramMuxer
6062
sessionManager datagramsession.Manager
6163
// datagramMuxer mux/demux datagrams from quic connection
62-
datagramMuxer *quicpogs.DatagramMuxerV2
64+
datagramMuxer *cfdquic.DatagramMuxerV2
6365
packetRouter *ingress.PacketRouter
6466
controlStreamHandler ControlStreamHandler
6567
connOptions *tunnelpogs.ConnectionOptions
6668
connIndex uint8
6769

68-
udpUnregisterTimeout time.Duration
69-
streamWriteTimeout time.Duration
70+
rpcTimeout time.Duration
71+
streamWriteTimeout time.Duration
7072
}
7173

7274
// NewQUICConnection returns a new instance of QUICConnection.
@@ -82,7 +84,7 @@ func NewQUICConnection(
8284
controlStreamHandler ControlStreamHandler,
8385
logger *zerolog.Logger,
8486
packetRouterConfig *ingress.GlobalRouterConfig,
85-
udpUnregisterTimeout time.Duration,
87+
rpcTimeout time.Duration,
8688
streamWriteTimeout time.Duration,
8789
) (*QUICConnection, error) {
8890
udpConn, err := createUDPConnForConnIndex(connIndex, localAddr, logger)
@@ -104,7 +106,7 @@ func NewQUICConnection(
104106
}
105107

106108
sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity)
107-
datagramMuxer := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan)
109+
datagramMuxer := cfdquic.NewDatagramMuxerV2(session, logger, sessionDemuxChan)
108110
sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan)
109111
packetRouter := ingress.NewPacketRouter(packetRouterConfig, datagramMuxer, logger)
110112

@@ -118,7 +120,7 @@ func NewQUICConnection(
118120
controlStreamHandler: controlStreamHandler,
119121
connOptions: connOptions,
120122
connIndex: connIndex,
121-
udpUnregisterTimeout: udpUnregisterTimeout,
123+
rpcTimeout: rpcTimeout,
122124
streamWriteTimeout: streamWriteTimeout,
123125
}, nil
124126
}
@@ -198,15 +200,16 @@ func (q *QUICConnection) acceptStream(ctx context.Context) error {
198200

199201
func (q *QUICConnection) runStream(quicStream quic.Stream) {
200202
ctx := quicStream.Context()
201-
stream := quicpogs.NewSafeStreamCloser(quicStream, q.streamWriteTimeout, q.logger)
203+
stream := cfdquic.NewSafeStreamCloser(quicStream, q.streamWriteTimeout, q.logger)
202204
defer stream.Close()
203205

204206
// we are going to fuse readers/writers from stream <- cloudflared -> origin, and we want to guarantee that
205207
// code executed in the code path of handleStream don't trigger an earlier close to the downstream write stream.
206208
// So, we wrap the stream with a no-op write closer and only this method can actually close write side of the stream.
207209
// A call to close will simulate a close to the read-side, which will fail subsequent reads.
208210
noCloseStream := &nopCloserReadWriter{ReadWriteCloser: stream}
209-
if err := q.handleStream(ctx, noCloseStream); err != nil {
211+
ss := rpcquic.NewCloudflaredServer(q.handleDataStream, q, q, q.rpcTimeout)
212+
if err := ss.Serve(ctx, noCloseStream); err != nil {
210213
q.logger.Debug().Err(err).Msg("Failed to handle QUIC stream")
211214

212215
// if we received an error at this level, then close write side of stream with an error, which will result in
@@ -215,30 +218,7 @@ func (q *QUICConnection) runStream(quicStream quic.Stream) {
215218
}
216219
}
217220

218-
func (q *QUICConnection) handleStream(ctx context.Context, stream io.ReadWriteCloser) error {
219-
signature, err := quicpogs.DetermineProtocol(stream)
220-
if err != nil {
221-
return err
222-
}
223-
switch signature {
224-
case quicpogs.DataStreamProtocolSignature:
225-
reqServerStream, err := quicpogs.NewRequestServerStream(stream, signature)
226-
if err != nil {
227-
return err
228-
}
229-
return q.handleDataStream(ctx, reqServerStream)
230-
case quicpogs.RPCStreamProtocolSignature:
231-
rpcStream, err := quicpogs.NewRPCServerStream(stream, signature)
232-
if err != nil {
233-
return err
234-
}
235-
return q.handleRPCStream(rpcStream)
236-
default:
237-
return fmt.Errorf("unknown protocol %v", signature)
238-
}
239-
}
240-
241-
func (q *QUICConnection) handleDataStream(ctx context.Context, stream *quicpogs.RequestServerStream) error {
221+
func (q *QUICConnection) handleDataStream(ctx context.Context, stream *rpcquic.RequestServerStream) error {
242222
request, err := stream.ReadConnectRequestData()
243223
if err != nil {
244224
return err
@@ -264,22 +244,22 @@ func (q *QUICConnection) handleDataStream(ctx context.Context, stream *quicpogs.
264244
// dispatchRequest will dispatch the request depending on the type and returns an error if it occurs.
265245
// More importantly, it also tells if the during processing of the request the ConnectResponse metadata was sent downstream.
266246
// This is important since it informs
267-
func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.RequestServerStream, err error, request *quicpogs.ConnectRequest) (error, bool) {
247+
func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *rpcquic.RequestServerStream, err error, request *pogs.ConnectRequest) (error, bool) {
268248
originProxy, err := q.orchestrator.GetOriginProxy()
269249
if err != nil {
270250
return err, false
271251
}
272252

273253
switch request.Type {
274-
case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket:
254+
case pogs.ConnectionTypeHTTP, pogs.ConnectionTypeWebsocket:
275255
tracedReq, err := buildHTTPRequest(ctx, request, stream, q.connIndex, q.logger)
276256
if err != nil {
277257
return err, false
278258
}
279259
w := newHTTPResponseAdapter(stream)
280-
return originProxy.ProxyHTTP(&w, tracedReq, request.Type == quicpogs.ConnectionTypeWebsocket), w.connectResponseSent
260+
return originProxy.ProxyHTTP(&w, tracedReq, request.Type == pogs.ConnectionTypeWebsocket), w.connectResponseSent
281261

282-
case quicpogs.ConnectionTypeTCP:
262+
case pogs.ConnectionTypeTCP:
283263
rwa := &streamReadWriteAcker{RequestServerStream: stream}
284264
metadata := request.MetadataMap()
285265
return originProxy.ProxyTCP(ctx, rwa, &TCPRequest{
@@ -293,14 +273,6 @@ func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.R
293273
}
294274
}
295275

296-
func (q *QUICConnection) handleRPCStream(rpcStream *quicpogs.RPCServerStream) error {
297-
if err := rpcStream.Serve(q, q, q.logger); err != nil {
298-
q.logger.Err(err).Msg("failed handling RPC stream")
299-
}
300-
301-
return nil
302-
}
303-
304276
// RegisterUdpSession is the RPC method invoked by edge to register and run a session
305277
func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration, traceContext string) (*tunnelpogs.RegisterUdpSessionResponse, error) {
306278
traceCtx := tracing.NewTracedContext(ctx, traceContext, q.logger)
@@ -377,9 +349,9 @@ func (q *QUICConnection) closeUDPSession(ctx context.Context, sessionID uuid.UUI
377349
return
378350
}
379351

380-
stream := quicpogs.NewSafeStreamCloser(quicStream, q.streamWriteTimeout, q.logger)
352+
stream := cfdquic.NewSafeStreamCloser(quicStream, q.streamWriteTimeout, q.logger)
381353
defer stream.Close()
382-
rpcClientStream, err := quicpogs.NewRPCClientStream(ctx, stream, q.udpUnregisterTimeout, q.logger)
354+
rpcClientStream, err := rpcquic.NewSessionClient(ctx, stream, q.rpcTimeout)
383355
if err != nil {
384356
// Log this at debug because this is not an error if session was closed due to lost connection
385357
// with edge
@@ -408,16 +380,16 @@ func (q *QUICConnection) UpdateConfiguration(ctx context.Context, version int32,
408380
// streamReadWriteAcker is a light wrapper over QUIC streams with a callback to send response back to
409381
// the client.
410382
type streamReadWriteAcker struct {
411-
*quicpogs.RequestServerStream
383+
*rpcquic.RequestServerStream
412384
connectResponseSent bool
413385
}
414386

415387
// AckConnection acks response back to the proxy.
416388
func (s *streamReadWriteAcker) AckConnection(tracePropagation string) error {
417-
metadata := []quicpogs.Metadata{}
389+
metadata := []pogs.Metadata{}
418390
// Only add tracing if provided by origintunneld
419391
if tracePropagation != "" {
420-
metadata = append(metadata, quicpogs.Metadata{
392+
metadata = append(metadata, pogs.Metadata{
421393
Key: tracing.CanonicalCloudflaredTracingHeader,
422394
Val: tracePropagation,
423395
})
@@ -428,12 +400,12 @@ func (s *streamReadWriteAcker) AckConnection(tracePropagation string) error {
428400

429401
// httpResponseAdapter translates responses written by the HTTP Proxy into ones that can be used in QUIC.
430402
type httpResponseAdapter struct {
431-
*quicpogs.RequestServerStream
403+
*rpcquic.RequestServerStream
432404
headers http.Header
433405
connectResponseSent bool
434406
}
435407

436-
func newHTTPResponseAdapter(s *quicpogs.RequestServerStream) httpResponseAdapter {
408+
func newHTTPResponseAdapter(s *rpcquic.RequestServerStream) httpResponseAdapter {
437409
return httpResponseAdapter{RequestServerStream: s, headers: make(http.Header)}
438410
}
439411

@@ -442,12 +414,12 @@ func (hrw *httpResponseAdapter) AddTrailer(trailerName, trailerValue string) {
442414
}
443415

444416
func (hrw *httpResponseAdapter) WriteRespHeaders(status int, header http.Header) error {
445-
metadata := make([]quicpogs.Metadata, 0)
446-
metadata = append(metadata, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(status)})
417+
metadata := make([]pogs.Metadata, 0)
418+
metadata = append(metadata, pogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(status)})
447419
for k, vv := range header {
448420
for _, v := range vv {
449421
httpHeaderKey := fmt.Sprintf("%s:%s", HTTPHeaderKey, k)
450-
metadata = append(metadata, quicpogs.Metadata{Key: httpHeaderKey, Val: v})
422+
metadata = append(metadata, pogs.Metadata{Key: httpHeaderKey, Val: v})
451423
}
452424
}
453425

@@ -483,17 +455,17 @@ func (hrw *httpResponseAdapter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
483455
}
484456

485457
func (hrw *httpResponseAdapter) WriteErrorResponse(err error) {
486-
hrw.WriteConnectResponseData(err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)})
458+
hrw.WriteConnectResponseData(err, pogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)})
487459
}
488460

489-
func (hrw *httpResponseAdapter) WriteConnectResponseData(respErr error, metadata ...quicpogs.Metadata) error {
461+
func (hrw *httpResponseAdapter) WriteConnectResponseData(respErr error, metadata ...pogs.Metadata) error {
490462
hrw.connectResponseSent = true
491463
return hrw.RequestServerStream.WriteConnectResponseData(respErr, metadata...)
492464
}
493465

494466
func buildHTTPRequest(
495467
ctx context.Context,
496-
connectRequest *quicpogs.ConnectRequest,
468+
connectRequest *pogs.ConnectRequest,
497469
body io.ReadCloser,
498470
connIndex uint8,
499471
log *zerolog.Logger,
@@ -502,7 +474,7 @@ func buildHTTPRequest(
502474
dest := connectRequest.Dest
503475
method := metadata[HTTPMethodKey]
504476
host := metadata[HTTPHostKey]
505-
isWebsocket := connectRequest.Type == quicpogs.ConnectionTypeWebsocket
477+
isWebsocket := connectRequest.Type == pogs.ConnectionTypeWebsocket
506478

507479
req, err := http.NewRequestWithContext(ctx, method, dest, body)
508480
if err != nil {
@@ -597,19 +569,19 @@ func (np *nopCloserReadWriter) Close() error {
597569

598570
// muxerWrapper wraps DatagramMuxerV2 to satisfy the packet.FunnelUniPipe interface
599571
type muxerWrapper struct {
600-
muxer *quicpogs.DatagramMuxerV2
572+
muxer *cfdquic.DatagramMuxerV2
601573
}
602574

603575
func (rp *muxerWrapper) SendPacket(dst netip.Addr, pk packet.RawPacket) error {
604-
return rp.muxer.SendPacket(quicpogs.RawPacket(pk))
576+
return rp.muxer.SendPacket(cfdquic.RawPacket(pk))
605577
}
606578

607579
func (rp *muxerWrapper) ReceivePacket(ctx context.Context) (packet.RawPacket, error) {
608580
pk, err := rp.muxer.ReceivePacket(ctx)
609581
if err != nil {
610582
return packet.RawPacket{}, err
611583
}
612-
rawPacket, ok := pk.(quicpogs.RawPacket)
584+
rawPacket, ok := pk.(cfdquic.RawPacket)
613585
if ok {
614586
return packet.RawPacket(rawPacket), nil
615587
}

0 commit comments

Comments
 (0)