diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 898b034c..75b39209 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -21,7 +21,6 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/cmd/cloudflared/config" "github.com/cloudflare/cloudflared/cmd/cloudflared/updater" - "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/dbconnect" "github.com/cloudflare/cloudflared/h2mux" "github.com/cloudflare/cloudflared/hello" @@ -32,10 +31,8 @@ import ( "github.com/cloudflare/cloudflared/socks" "github.com/cloudflare/cloudflared/sshlog" "github.com/cloudflare/cloudflared/sshserver" - "github.com/cloudflare/cloudflared/supervisor" "github.com/cloudflare/cloudflared/tlsconfig" "github.com/cloudflare/cloudflared/tunneldns" - "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/cloudflare/cloudflared/websocket" "github.com/coreos/go-systemd/daemon" @@ -93,8 +90,6 @@ const ( // bastionFlag is to enable bastion, or jump host, operation bastionFlag = "bastion" - noIntentMsg = "The --intent argument is required. Cloudflared looks up an Intent to determine what configuration to use (i.e. which tunnels to start). If you don't have any Intents yet, you can use a placeholder Intent Label for now. Then, when you make an Intent with that label, cloudflared will get notified and open the tunnels you specified in that Intent." - debugLevelWarning = "At debug level, request URL, method, protocol, content legnth and header will be logged. " + "Response status, content length and header will also be logged in debug level." ) @@ -322,10 +317,6 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan cancel() }() - if c.IsSet("use-declarative-tunnels") { - return startDeclarativeTunnel(ctx, c, cloudflaredID, buildInfo, &listeners, logger) - } - // update needs to be after DNS proxy is up to resolve equinox server address if updater.IsAutoupdateEnabled(c, logger) { logger.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq")) @@ -500,124 +491,6 @@ func isProxyDestinationConfigured(staticHost string, c *cli.Context) bool { return staticHost != "" || c.IsSet(bastionFlag) } -func startDeclarativeTunnel(ctx context.Context, - c *cli.Context, - cloudflaredID uuid.UUID, - buildInfo *buildinfo.BuildInfo, - listeners *gracenet.Net, - logger logger.Service, -) error { - reverseProxyOrigin, err := defaultOriginConfig(c) - if err != nil { - logger.Errorf("%s", err) - return err - } - reverseProxyConfig, err := pogs.NewReverseProxyConfig( - c.String("hostname"), - reverseProxyOrigin, - c.Uint64("retries"), - c.Duration("proxy-connection-timeout"), - c.Uint64("compression-quality"), - ) - if err != nil { - logger.Errorf("Cannot initialize default client config because reverse proxy config is invalid: %s", err) - return err - } - defaultClientConfig := &pogs.ClientConfig{ - Version: pogs.InitVersion(), - SupervisorConfig: &pogs.SupervisorConfig{ - AutoUpdateFrequency: c.Duration("autoupdate-freq"), - MetricsUpdateFrequency: c.Duration("metrics-update-freq"), - GracePeriod: c.Duration("grace-period"), - }, - EdgeConnectionConfig: &pogs.EdgeConnectionConfig{ - NumHAConnections: uint8(c.Int("ha-connections")), - HeartbeatInterval: c.Duration("heartbeat-interval"), - Timeout: c.Duration("dial-edge-timeout"), - MaxFailedHeartbeats: c.Uint64("heartbeat-count"), - }, - DoHProxyConfigs: []*pogs.DoHProxyConfig{}, - ReverseProxyConfigs: []*pogs.ReverseProxyConfig{reverseProxyConfig}, - } - - autoupdater := updater.NewAutoUpdater(defaultClientConfig.SupervisorConfig.AutoUpdateFrequency, listeners, logger) - - originCert, err := getOriginCert(c, logger) - if err != nil { - logger.Errorf("error getting origin cert: %s", err) - return err - } - toEdgeTLSConfig, err := tlsconfig.CreateTunnelConfig(c) - if err != nil { - logger.Errorf("unable to create TLS config to connect with edge: %s", err) - return err - } - - tags, err := NewTagSliceFromCLI(c.StringSlice("tag")) - if err != nil { - logger.Errorf("unable to parse tag: %s", err) - return err - } - - intentLabel := c.String("intent") - if intentLabel == "" { - logger.Error("--intent was empty") - return fmt.Errorf(noIntentMsg) - } - - cloudflaredConfig := &connection.CloudflaredConfig{ - BuildInfo: buildInfo, - CloudflaredID: cloudflaredID, - IntentLabel: intentLabel, - Tags: tags, - } - - serviceDiscoverer, err := serviceDiscoverer(c, logger) - if err != nil { - logger.Errorf("unable to create service discoverer: %s", err) - return err - } - supervisor, err := supervisor.NewSupervisor(defaultClientConfig, originCert, toEdgeTLSConfig, - serviceDiscoverer, cloudflaredConfig, autoupdater, updater.SupportAutoUpdate(logger), logger) - if err != nil { - logger.Errorf("unable to create Supervisor: %s", err) - return err - } - return supervisor.Run(ctx) -} - -func defaultOriginConfig(c *cli.Context) (pogs.OriginConfig, error) { - if c.IsSet("hello-world") { - return &pogs.HelloWorldOriginConfig{}, nil - } - originConfig := &pogs.HTTPOriginConfig{ - TCPKeepAlive: c.Duration("proxy-tcp-keepalive"), - DialDualStack: !c.Bool("proxy-no-happy-eyeballs"), - TLSHandshakeTimeout: c.Duration("proxy-tls-timeout"), - TLSVerify: !c.Bool("no-tls-verify"), - OriginCAPool: c.String("origin-ca-pool"), - OriginServerName: c.String("origin-server-name"), - MaxIdleConnections: c.Uint64("proxy-keepalive-connections"), - IdleConnectionTimeout: c.Duration("proxy-keepalive-timeout"), - ProxyConnectionTimeout: c.Duration("proxy-connection-timeout"), - ExpectContinueTimeout: c.Duration("proxy-expect-continue-timeout"), - ChunkedEncoding: c.Bool("no-chunked-encoding"), - } - if c.IsSet("unix-socket") { - unixSocket, err := config.ValidateUnixSocket(c) - if err != nil { - return nil, errors.Wrap(err, "error validating --unix-socket") - } - originConfig.URLString = unixSocket - } - originAddr, err := config.ValidateUrl(c) - if err != nil { - return nil, errors.Wrap(err, "error validating origin URL") - } - originConfig.URLString = originAddr - return originConfig, nil -} - func waitToShutdown(wg *sync.WaitGroup, errC chan error, shutdownC, graceShutdownC chan struct{}, @@ -1064,18 +937,6 @@ func tunnelFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"TUNNEL_TRACE_OUTPUT"}, Hidden: shouldHide, }), - altsrc.NewBoolFlag(&cli.BoolFlag{ - Name: "use-declarative-tunnels", - Usage: "Test establishing connections with declarative tunnel methods.", - EnvVars: []string{"TUNNEL_USE_DECLARATIVE"}, - Hidden: true, - }), - altsrc.NewStringFlag(&cli.StringFlag{ - Name: "intent", - Usage: "The label of an Intent from which `cloudflared` should gets its tunnels from. Intents can be created in the Origin Registry UI.", - EnvVars: []string{"TUNNEL_INTENT"}, - Hidden: true, - }), altsrc.NewBoolFlag(&cli.BoolFlag{ Name: "use-reconnect-token", Usage: "Test reestablishing connections with the new 'reconnect token' flow.", diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index a085f1b5..274eafa0 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -14,7 +14,6 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo" "github.com/cloudflare/cloudflared/cmd/cloudflared/config" - "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/origin" "github.com/cloudflare/cloudflared/tlsconfig" @@ -290,15 +289,6 @@ func prepareTunnelConfig( }, nil } -func serviceDiscoverer(c *cli.Context, logger logger.Service) (*edgediscovery.Edge, error) { - // If --edge is specfied, resolve edge server addresses - if len(c.StringSlice("edge")) > 0 { - return edgediscovery.StaticEdge(logger, c.StringSlice("edge")) - } - // Otherwise lookup edge server addresses through service discovery - return edgediscovery.ResolveEdge(logger) -} - func isRunningFromTerminal() bool { return terminal.IsTerminal(int(os.Stdout.Fd())) } diff --git a/connection/connection.go b/connection/connection.go deleted file mode 100644 index a49bf966..00000000 --- a/connection/connection.go +++ /dev/null @@ -1,57 +0,0 @@ -package connection - -import ( - "context" - "net" - "time" - - "github.com/google/uuid" - "github.com/pkg/errors" - - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/logger" - tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" -) - -const ( - openStreamTimeout = 30 * time.Second -) - -type Connection struct { - id uuid.UUID - muxer *h2mux.Muxer - addr *net.TCPAddr - isLongLived bool - longLivedID int -} - -func newConnection(muxer *h2mux.Muxer, addr *net.TCPAddr) (*Connection, error) { - id, err := uuid.NewRandom() - if err != nil { - return nil, err - } - return &Connection{ - id: id, - muxer: muxer, - addr: addr, - }, nil -} - -func (c *Connection) Serve(ctx context.Context) error { - // Serve doesn't return until h2mux is shutdown - return c.muxer.Serve(ctx) -} - -// Connect is used to establish connections with cloudflare's edge network -func (c *Connection) Connect(ctx context.Context, parameters *tunnelpogs.ConnectParameters, logger logger.Service) (tunnelpogs.ConnectResult, error) { - tsClient, err := NewRPCClient(ctx, c.muxer, logger, openStreamTimeout) - if err != nil { - return nil, errors.Wrap(err, "cannot create new RPC connection") - } - defer tsClient.Close() - return tsClient.Connect(ctx, parameters) -} - -func (c *Connection) Shutdown() { - c.muxer.Shutdown() -} diff --git a/connection/manager.go b/connection/manager.go deleted file mode 100644 index 00f85ba7..00000000 --- a/connection/manager.go +++ /dev/null @@ -1,302 +0,0 @@ -package connection - -import ( - "context" - "crypto/tls" - "fmt" - "sync" - "time" - - "github.com/google/uuid" - "github.com/pkg/errors" - "github.com/prometheus/client_golang/prometheus" - - "github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo" - "github.com/cloudflare/cloudflared/edgediscovery" - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/logger" - "github.com/cloudflare/cloudflared/streamhandler" - tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" -) - -const ( - quickStartLink = "https://developers.cloudflare.com/argo-tunnel/quickstart/" - faqLink = "https://developers.cloudflare.com/argo-tunnel/faq/" - defaultRetryAfter = time.Second * 5 - packageNamespace = "connection" - edgeManagerSubsystem = "edgemanager" -) - -// EdgeManager manages connections with the edge -type EdgeManager struct { - // streamHandler handles stream opened by the edge - streamHandler *streamhandler.StreamHandler - // TLSConfig is the TLS configuration to connect with edge - tlsConfig *tls.Config - // cloudflaredConfig is the cloudflared configuration that is determined when the process first starts - cloudflaredConfig *CloudflaredConfig - // serviceDiscoverer returns the next edge addr to connect to - serviceDiscoverer *edgediscovery.Edge - // state is attributes of ConnectionManager that can change during runtime. - state *edgeManagerState - - logger logger.Service - - metrics *metrics -} - -type metrics struct { - // activeStreams is a gauge shared by all muxers of this process to expose the total number of active streams - activeStreams prometheus.Gauge -} - -func newMetrics(namespace, subsystem string) *metrics { - return &metrics{ - activeStreams: h2mux.NewActiveStreamsMetrics(namespace, subsystem), - } -} - -// EdgeManagerConfigurable is the configurable attributes of a EdgeConnectionManager -type EdgeManagerConfigurable struct { - TunnelHostnames []h2mux.TunnelHostname - *tunnelpogs.EdgeConnectionConfig -} - -type CloudflaredConfig struct { - CloudflaredID uuid.UUID - Tags []tunnelpogs.Tag - BuildInfo *buildinfo.BuildInfo - IntentLabel string -} - -func NewEdgeManager( - streamHandler *streamhandler.StreamHandler, - edgeConnMgrConfigurable *EdgeManagerConfigurable, - userCredential []byte, - tlsConfig *tls.Config, - serviceDiscoverer *edgediscovery.Edge, - cloudflaredConfig *CloudflaredConfig, - logger logger.Service, -) *EdgeManager { - return &EdgeManager{ - streamHandler: streamHandler, - tlsConfig: tlsConfig, - cloudflaredConfig: cloudflaredConfig, - serviceDiscoverer: serviceDiscoverer, - state: newEdgeConnectionManagerState(edgeConnMgrConfigurable, userCredential), - logger: logger, - metrics: newMetrics(packageNamespace, edgeManagerSubsystem), - } -} - -func (em *EdgeManager) Run(ctx context.Context) error { - defer em.shutdown() - - // Currently, declarative tunnels don't have any concept of a stable connection - // Each edge connection is transient and when it dies, it is replaced by a different one, - // not restarted. - // So in the future we should really change this so that n connections are stored individually - connIndex := 0 - for { - select { - case <-ctx.Done(): - return errors.Wrap(ctx.Err(), "EdgeConnectionManager terminated") - default: - time.Sleep(1 * time.Second) - } - // Create/delete connection one at a time, so we don't need to adjust for connections that are being created/deleted - // in shouldCreateConnection or shouldReduceConnection calculation - if em.state.shouldCreateConnection(em.serviceDiscoverer.AvailableAddrs()) { - if connErr := em.newConnection(ctx, connIndex); connErr != nil { - if !connErr.ShouldRetry { - em.logger.Errorf("connectionManager: %s with error: %s", em.noRetryMessage(), connErr) - return connErr - } - em.logger.Errorf("connectionManager: cannot create new connection: %s", connErr) - } else { - connIndex++ - } - } else if em.state.shouldReduceConnection() { - if err := em.closeConnection(ctx); err != nil { - em.logger.Errorf("connectionManager: cannot close connection: %s", err) - } - } - } -} - -func (em *EdgeManager) UpdateConfigurable(newConfigurable *EdgeManagerConfigurable) { - em.logger.Infof("New edge connection manager configuration %+v", newConfigurable) - em.state.updateConfigurable(newConfigurable) -} - -func (em *EdgeManager) newConnection(ctx context.Context, index int) *tunnelpogs.ConnectError { - edgeTCPAddr, err := em.serviceDiscoverer.GetAddr(index) - if err != nil { - return retryConnection(fmt.Sprintf("edge address discovery error: %v", err)) - } - configurable := em.state.getConfigurable() - edgeConn, err := DialEdge(ctx, configurable.Timeout, em.tlsConfig, edgeTCPAddr) - if err != nil { - return retryConnection(fmt.Sprintf("dial edge error: %v", err)) - } - // Establish a muxed connection with the edge - // Client mux handshake with agent server - muxer, err := h2mux.Handshake(edgeConn, edgeConn, h2mux.MuxerConfig{ - Timeout: configurable.Timeout, - Handler: em.streamHandler, - IsClient: true, - HeartbeatInterval: configurable.HeartbeatInterval, - MaxHeartbeats: configurable.MaxFailedHeartbeats, - Logger: em.logger, - }, em.metrics.activeStreams) - if err != nil { - retryConnection(fmt.Sprintf("couldn't perform handshake with edge: %v", err)) - } - - h2muxConn, err := newConnection(muxer, edgeTCPAddr) - if err != nil { - return retryConnection(fmt.Sprintf("couldn't create h2mux connection: %v", err)) - } - - go em.serveConn(ctx, h2muxConn) - - connResult, err := h2muxConn.Connect(ctx, &tunnelpogs.ConnectParameters{ - CloudflaredID: em.cloudflaredConfig.CloudflaredID, - CloudflaredVersion: em.cloudflaredConfig.BuildInfo.CloudflaredVersion, - NumPreviousAttempts: 0, - OriginCert: em.state.getUserCredential(), - IntentLabel: em.cloudflaredConfig.IntentLabel, - Tags: em.cloudflaredConfig.Tags, - }, em.logger) - if err != nil { - h2muxConn.Shutdown() - return retryConnection(fmt.Sprintf("couldn't connect to edge: %v", err)) - } - - if connErr := connResult.ConnectError(); connErr != nil { - return connErr - } - - em.state.newConnection(h2muxConn) - em.logger.Infof("connectionManager: connected to %s", connResult.ConnectedTo()) - - if connResult.ClientConfig() != nil { - em.streamHandler.UseConfiguration(ctx, connResult.ClientConfig()) - } - return nil -} - -func (em *EdgeManager) closeConnection(ctx context.Context) error { - conn := em.state.getFirstConnection() - if conn == nil { - return fmt.Errorf("no connection to close") - } - conn.Shutdown() - // teardown will be handled by EdgeManager.serveConn in another goroutine - return nil -} - -func (em *EdgeManager) serveConn(ctx context.Context, conn *Connection) { - err := conn.Serve(ctx) - em.logger.Errorf("connectionManager: Connection closed: %s", err) - em.state.closeConnection(conn) - em.serviceDiscoverer.GiveBack(conn.addr) -} - -func (em *EdgeManager) noRetryMessage() string { - messageTemplate := "cloudflared could not register an Argo Tunnel on your account. Please confirm the following before trying again:" + - "1. You have Argo Smart Routing enabled in your account, See Enable Argo section of %s." + - "2. Your credential at %s is still valid. See %s." - return fmt.Sprintf(messageTemplate, quickStartLink, em.state.getConfigurable().UserCredentialPath, faqLink) -} - -func (em *EdgeManager) shutdown() { - em.state.shutdown() -} - -type edgeManagerState struct { - sync.RWMutex - configurable *EdgeManagerConfigurable - userCredential []byte - conns map[uuid.UUID]*Connection -} - -func newEdgeConnectionManagerState(configurable *EdgeManagerConfigurable, userCredential []byte) *edgeManagerState { - return &edgeManagerState{ - configurable: configurable, - userCredential: userCredential, - conns: make(map[uuid.UUID]*Connection), - } -} - -func (ems *edgeManagerState) shouldCreateConnection(availableEdgeAddrs int) bool { - ems.RLock() - defer ems.RUnlock() - expectedHAConns := int(ems.configurable.NumHAConnections) - if availableEdgeAddrs < expectedHAConns { - expectedHAConns = availableEdgeAddrs - } - return len(ems.conns) < expectedHAConns -} - -func (ems *edgeManagerState) shouldReduceConnection() bool { - ems.RLock() - defer ems.RUnlock() - return uint8(len(ems.conns)) > ems.configurable.NumHAConnections -} - -func (ems *edgeManagerState) newConnection(conn *Connection) { - ems.Lock() - defer ems.Unlock() - ems.conns[conn.id] = conn -} - -func (ems *edgeManagerState) closeConnection(conn *Connection) { - ems.Lock() - defer ems.Unlock() - delete(ems.conns, conn.id) -} - -func (ems *edgeManagerState) getFirstConnection() *Connection { - ems.RLock() - defer ems.RUnlock() - - for _, conn := range ems.conns { - return conn - } - return nil -} - -func (ems *edgeManagerState) shutdown() { - ems.Lock() - defer ems.Unlock() - for _, conn := range ems.conns { - conn.Shutdown() - } -} - -func (ems *edgeManagerState) getConfigurable() *EdgeManagerConfigurable { - ems.Lock() - defer ems.Unlock() - return ems.configurable -} - -func (ems *edgeManagerState) updateConfigurable(newConfigurable *EdgeManagerConfigurable) { - ems.Lock() - defer ems.Unlock() - ems.configurable = newConfigurable -} - -func (ems *edgeManagerState) getUserCredential() []byte { - ems.RLock() - defer ems.RUnlock() - return ems.userCredential -} - -func retryConnection(cause string) *tunnelpogs.ConnectError { - return &tunnelpogs.ConnectError{ - Cause: cause, - RetryAfter: defaultRetryAfter, - ShouldRetry: true, - } -} diff --git a/connection/manager_test.go b/connection/manager_test.go deleted file mode 100644 index 791c5add..00000000 --- a/connection/manager_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package connection - -import ( - "net" - "testing" - "time" - - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - - "github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo" - "github.com/cloudflare/cloudflared/edgediscovery" - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/logger" - "github.com/cloudflare/cloudflared/streamhandler" - "github.com/cloudflare/cloudflared/tunnelrpc/pogs" -) - -var ( - configurable = &EdgeManagerConfigurable{ - []h2mux.TunnelHostname{ - "http.example.com", - "ws.example.com", - "hello.example.com", - }, - &pogs.EdgeConnectionConfig{ - NumHAConnections: 1, - HeartbeatInterval: 1 * time.Second, - Timeout: 5 * time.Second, - MaxFailedHeartbeats: 3, - UserCredentialPath: "/etc/cloudflared/cert.pem", - }, - } - cloudflaredConfig = &CloudflaredConfig{ - CloudflaredID: uuid.New(), - Tags: []pogs.Tag{ - {Name: "pool", Value: "east-6"}, - }, - BuildInfo: &buildinfo.BuildInfo{ - GoOS: "linux", - GoVersion: "1.12", - GoArch: "amd64", - CloudflaredVersion: "2019.6.0", - }, - } -) - -func mockEdgeManager() *EdgeManager { - newConfigChan := make(chan<- *pogs.ClientConfig) - useConfigResultChan := make(<-chan *pogs.UseConfigurationResult) - logger := logger.NewOutputWriter(logger.NewMockWriteManager()) - edge := edgediscovery.MockEdge(logger, []*net.TCPAddr{}) - return NewEdgeManager( - streamhandler.NewStreamHandler(newConfigChan, useConfigResultChan, logger), - configurable, - []byte{}, - nil, - edge, - cloudflaredConfig, - logger, - ) -} - -func TestUpdateConfigurable(t *testing.T) { - m := mockEdgeManager() - newConfigurable := &EdgeManagerConfigurable{ - []h2mux.TunnelHostname{ - "second.example.com", - }, - &pogs.EdgeConnectionConfig{ - NumHAConnections: 2, - }, - } - m.UpdateConfigurable(newConfigurable) - - assert.Equal(t, newConfigurable, m.state.getConfigurable()) -} diff --git a/go.mod b/go.mod index 85aee5c3..c849d998 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,7 @@ require ( gopkg.in/square/go-jose.v2 v2.4.0 // indirect gopkg.in/urfave/cli.v2 v2.0.0-20180128181224-d604b6ffeee8 gopkg.in/yaml.v2 v2.2.4 - zombiezen.com/go/capnproto2 v0.0.0-20180616160808-7cfd211c19c7 + zombiezen.com/go/capnproto2 v2.18.0+incompatible ) // ../../go/pkg/mod/github.com/coredns/coredns@v1.2.0/plugin/metrics/metrics.go:40:49: too many arguments in call to prometheus.NewProcessCollector diff --git a/go.sum b/go.sum index 774e3261..4a33c5ab 100644 --- a/go.sum +++ b/go.sum @@ -9,9 +9,9 @@ github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFD github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= -github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1 h1:RKnVV4C7qoN/sToLX2y1dqH7T6kKLMHcwRJlgwb9Ggk= github.com/acmacalister/skittles v0.0.0-20160609003031-7423546701e1/go.mod h1:gI5CyA/CEnS6eqNV22rqs4dG3aGfaSbXgPORIlwr2r0= +github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -285,3 +285,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= zombiezen.com/go/capnproto2 v0.0.0-20180616160808-7cfd211c19c7 h1:CZoOFlTPbKfAShKYrMuUfYbnXexFT1rYRUX1SPnrdE4= zombiezen.com/go/capnproto2 v0.0.0-20180616160808-7cfd211c19c7/go.mod h1:TMGa8HWGJkXiq4nHe9Zu/JgRF5oUtg4XizFC+Vexbec= +zombiezen.com/go/capnproto2 v2.18.0+incompatible h1:mwfXZniffG5mXokQGHUJWGnqIBggoPfT/CEwon9Yess= +zombiezen.com/go/capnproto2 v2.18.0+incompatible/go.mod h1:XO5Pr2SbXgqZwn0m0Ru54QBqpOf4K5AYBO+8LAOBQEQ= diff --git a/origin/tunnel.go b/origin/tunnel.go index a68dedc0..dc5bbb78 100644 --- a/origin/tunnel.go +++ b/origin/tunnel.go @@ -25,7 +25,6 @@ import ( "github.com/cloudflare/cloudflared/h2mux" "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/signal" - "github.com/cloudflare/cloudflared/streamhandler" "github.com/cloudflare/cloudflared/tunnelrpc" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/cloudflare/cloudflared/validation" @@ -618,8 +617,8 @@ func (h *TunnelHandler) ServeStream(stream *h2mux.MuxedStream) error { return reqErr } - cfRay := streamhandler.FindCfRayHeader(req) - lbProbe := streamhandler.IsLBProbeRequest(req) + cfRay := findCfRayHeader(req) + lbProbe := isLBProbeRequest(req) h.logRequest(req, cfRay, lbProbe) var resp *http.Response @@ -833,3 +832,11 @@ func activeIncidentsMsg(incidents []Incident) string { return preamble + " " + strings.Join(incidentStrings, "; ") } + +func findCfRayHeader(h1 *http.Request) string { + return h1.Header.Get("Cf-Ray") +} + +func isLBProbeRequest(req *http.Request) bool { + return strings.HasPrefix(req.UserAgent(), lbProbeUserAgentPrefix) +} diff --git a/originservice/originservice.go b/originservice/originservice.go deleted file mode 100644 index 97fa69ef..00000000 --- a/originservice/originservice.go +++ /dev/null @@ -1,247 +0,0 @@ -// Package client defines and implements interface to proxy to HTTP, websocket and hello world origins -package originservice - -import ( - "bufio" - "crypto/tls" - "fmt" - "io" - "net" - "net/http" - "net/url" - "strconv" - "strings" - - "github.com/cloudflare/cloudflared/buffer" - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/hello" - "github.com/cloudflare/cloudflared/logger" - "github.com/cloudflare/cloudflared/websocket" - "github.com/pkg/errors" -) - -// OriginService is an interface to proxy requests to different type of origins -type OriginService interface { - Proxy(stream *h2mux.MuxedStream, req *http.Request) (resp *http.Response, err error) - URL() *url.URL - Summary() string - Shutdown() -} - -// HTTPService talks to origin using HTTP/HTTPS -type HTTPService struct { - client http.RoundTripper - originURL *url.URL - chunkedEncoding bool - bufferPool *buffer.Pool -} - -func NewHTTPService(transport http.RoundTripper, url *url.URL, chunkedEncoding bool) OriginService { - return &HTTPService{ - client: transport, - originURL: url, - chunkedEncoding: chunkedEncoding, - bufferPool: buffer.NewPool(512 * 1024), - } -} - -func (hc *HTTPService) Proxy(stream *h2mux.MuxedStream, req *http.Request) (*http.Response, error) { - const responseSourceOrigin = "origin" - - // Support for WSGI Servers by switching transfer encoding from chunked to gzip/deflate - if !hc.chunkedEncoding { - req.TransferEncoding = []string{"gzip", "deflate"} - cLength, err := strconv.Atoi(req.Header.Get("Content-Length")) - if err == nil { - req.ContentLength = int64(cLength) - } - } - - // Request origin to keep connection alive to improve performance - req.Header.Set("Connection", "keep-alive") - - resp, err := hc.client.RoundTrip(req) - if err != nil { - return nil, errors.Wrap(err, "error proxying request to HTTP origin") - } - defer resp.Body.Close() - - responseHeaders := h1ResponseToH2Response(resp) - responseHeaders = append(responseHeaders, h2mux.CreateResponseMetaHeader(h2mux.ResponseMetaHeaderField, responseSourceOrigin)) - err = stream.WriteHeaders(responseHeaders) - if err != nil { - return nil, errors.Wrap(err, "error writing response header to HTTP origin") - } - if isEventStream(resp) { - writeEventStream(stream, resp.Body) - } else { - // Use CopyBuffer, because Copy only allocates a 32KiB buffer, and cross-stream - // compression generates dictionary on first write - buf := hc.bufferPool.Get() - defer hc.bufferPool.Put(buf) - io.CopyBuffer(stream, resp.Body, buf) - } - return resp, nil -} - -func (hc *HTTPService) URL() *url.URL { - return hc.originURL -} - -func (hc *HTTPService) Summary() string { - return fmt.Sprintf("HTTP service listening on %s", hc.originURL) -} - -func (hc *HTTPService) Shutdown() {} - -// WebsocketService talks to origin using WS/WSS -type WebsocketService struct { - tlsConfig *tls.Config - originURL *url.URL - shutdownC chan struct{} -} - -func NewWebSocketService(tlsConfig *tls.Config, url *url.URL, logger logger.Service) (OriginService, error) { - listener, err := net.Listen("tcp", "127.0.0.1:") - if err != nil { - return nil, errors.Wrap(err, "cannot start Websocket Proxy Server") - } - shutdownC := make(chan struct{}) - go func() { - websocket.StartProxyServer(logger, listener, url.String(), shutdownC, websocket.DefaultStreamHandler) - }() - return &WebsocketService{ - tlsConfig: tlsConfig, - originURL: url, - shutdownC: shutdownC, - }, nil -} - -func (wsc *WebsocketService) Proxy(stream *h2mux.MuxedStream, req *http.Request) (*http.Response, error) { - if !websocket.IsWebSocketUpgrade(req) { - return nil, fmt.Errorf("request is not a websocket connection") - } - conn, response, err := websocket.ClientConnect(req, wsc.tlsConfig) - if err != nil { - return nil, err - } - defer conn.Close() - err = stream.WriteHeaders(h1ResponseToH2Response(response)) - if err != nil { - return nil, errors.Wrap(err, "error writing response header to websocket origin") - } - // Copy to/from stream to the undelying connection. Use the underlying - // connection because cloudflared doesn't operate on the message themselves - websocket.Stream(conn.UnderlyingConn(), stream) - return response, nil -} - -func (wsc *WebsocketService) URL() *url.URL { - return wsc.originURL -} - -func (wsc *WebsocketService) Summary() string { - return fmt.Sprintf("Websocket listening on %s", wsc.originURL) -} - -func (wsc *WebsocketService) Shutdown() { - close(wsc.shutdownC) -} - -// HelloWorldService talks to the hello world example origin -type HelloWorldService struct { - client http.RoundTripper - listener net.Listener - originURL *url.URL - shutdownC chan struct{} - bufferPool *buffer.Pool -} - -func NewHelloWorldService(transport http.RoundTripper, logger logger.Service) (OriginService, error) { - listener, err := hello.CreateTLSListener("127.0.0.1:") - if err != nil { - return nil, errors.Wrap(err, "cannot start Hello World Server") - } - shutdownC := make(chan struct{}) - go func() { - hello.StartHelloWorldServer(logger, listener, shutdownC) - }() - return &HelloWorldService{ - client: transport, - listener: listener, - originURL: &url.URL{ - Scheme: "https", - Host: listener.Addr().String(), - }, - shutdownC: shutdownC, - bufferPool: buffer.NewPool(512 * 1024), - }, nil -} - -func (hwc *HelloWorldService) Proxy(stream *h2mux.MuxedStream, req *http.Request) (*http.Response, error) { - // Request origin to keep connection alive to improve performance - req.Header.Set("Connection", "keep-alive") - resp, err := hwc.client.RoundTrip(req) - if err != nil { - return nil, errors.Wrap(err, "error proxying request to Hello World origin") - } - defer resp.Body.Close() - - err = stream.WriteHeaders(h1ResponseToH2Response(resp)) - if err != nil { - return nil, errors.Wrap(err, "error writing response header to Hello World origin") - } - - // Use CopyBuffer, because Copy only allocates a 32KiB buffer, and cross-stream - // compression generates dictionary on first write - buf := hwc.bufferPool.Get() - defer hwc.bufferPool.Put(buf) - io.CopyBuffer(stream, resp.Body, buf) - - return resp, nil -} - -func (hwc *HelloWorldService) URL() *url.URL { - return hwc.originURL -} - -func (hwc *HelloWorldService) Summary() string { - return fmt.Sprintf("Hello World service listening on %s", hwc.originURL) -} - -func (hwc *HelloWorldService) Shutdown() { - hwc.listener.Close() -} - -func isEventStream(resp *http.Response) bool { - // Check if content-type is text/event-stream. We need to check if the header value starts with text/event-stream - // because text/event-stream; charset=UTF-8 is also valid - // Ref: https://tools.ietf.org/html/rfc7231#section-3.1.1.1 - for _, contentType := range resp.Header["content-type"] { - if strings.HasPrefix(strings.ToLower(contentType), "text/event-stream") { - return true - } - } - return false -} - -func writeEventStream(stream *h2mux.MuxedStream, respBody io.ReadCloser) { - reader := bufio.NewReader(respBody) - for { - line, err := reader.ReadBytes('\n') - if err != nil { - break - } - stream.Write(line) - } -} - -func h1ResponseToH2Response(h1 *http.Response) (h2 []h2mux.Header) { - h2 = []h2mux.Header{{Name: ":status", Value: fmt.Sprintf("%d", h1.StatusCode)}} - for headerName, headerValues := range h1.Header { - for _, headerValue := range headerValues { - h2 = append(h2, h2mux.Header{Name: strings.ToLower(headerName), Value: headerValue}) - } - } - return -} diff --git a/originservice/originservice_test.go b/originservice/originservice_test.go deleted file mode 100644 index 2a1c0a3d..00000000 --- a/originservice/originservice_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package originservice - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestIsEventStream(t *testing.T) { - tests := []struct { - resp *http.Response - isEventStream bool - }{ - { - resp: &http.Response{}, - isEventStream: false, - }, - { - // isEventStream checks all headers - resp: &http.Response{ - Header: http.Header{ - "accept": []string{"text/html"}, - "content-type": []string{"text/event-stream"}, - }, - }, - isEventStream: true, - }, - { - // Content-Type and text/event-stream are case-insensitive. text/event-stream can be followed by OWS parameter - resp: &http.Response{ - Header: http.Header{ - "content-type": []string{"Text/event-stream;charset=utf-8"}, - }, - }, - isEventStream: true, - }, - { - // Content-Type and text/event-stream are case-insensitive. text/event-stream can be followed by OWS parameter - resp: &http.Response{ - Header: http.Header{ - "content-type": []string{"appication/json", "text/html", "Text/event-stream;charset=utf-8"}, - }, - }, - isEventStream: true, - }, - { - // Not an event stream because the content-type value doesn't start with text/event-stream - resp: &http.Response{ - Header: http.Header{ - "content-type": []string{" text/event-stream"}, - }, - }, - isEventStream: false, - }, - } - for _, test := range tests { - assert.Equal(t, test.isEventStream, isEventStream(test.resp), "Header: %v", test.resp.Header) - } -} diff --git a/streamhandler/request.go b/streamhandler/request.go deleted file mode 100644 index b6bb55d3..00000000 --- a/streamhandler/request.go +++ /dev/null @@ -1,34 +0,0 @@ -package streamhandler - -import ( - "net/http" - "net/url" - "strings" - - "github.com/cloudflare/cloudflared/h2mux" - "github.com/pkg/errors" -) - -const ( - lbProbeUserAgentPrefix = "Mozilla/5.0 (compatible; Cloudflare-Traffic-Manager/1.0; +https://www.cloudflare.com/traffic-manager/;" -) - -func FindCfRayHeader(h1 *http.Request) string { - return h1.Header.Get("Cf-Ray") -} - -func IsLBProbeRequest(req *http.Request) bool { - return strings.HasPrefix(req.UserAgent(), lbProbeUserAgentPrefix) -} - -func createRequest(stream *h2mux.MuxedStream, url *url.URL) (*http.Request, error) { - req, err := http.NewRequest(http.MethodGet, url.String(), h2mux.MuxedStreamReader{MuxedStream: stream}) - if err != nil { - return nil, errors.Wrap(err, "unexpected error from http.NewRequest") - } - err = h2mux.H2RequestHeadersToH1Request(stream.Headers, req) - if err != nil { - return nil, errors.Wrap(err, "invalid request received") - } - return req, nil -} diff --git a/streamhandler/stream_handler.go b/streamhandler/stream_handler.go deleted file mode 100644 index 2b40b0a1..00000000 --- a/streamhandler/stream_handler.go +++ /dev/null @@ -1,189 +0,0 @@ -package streamhandler - -import ( - "context" - "fmt" - "net/http" - "strconv" - - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/logger" - "github.com/cloudflare/cloudflared/tunnelhostnamemapper" - "github.com/cloudflare/cloudflared/tunnelrpc" - "github.com/cloudflare/cloudflared/tunnelrpc/pogs" - "github.com/pkg/errors" - "zombiezen.com/go/capnproto2/rpc" -) - -const ( - statusPseudoHeader = ":status" -) - -type httpErrorStatus struct { - status string - text []byte -} - -var ( - statusBadRequest = newHTTPErrorStatus(http.StatusBadRequest) - statusNotFound = newHTTPErrorStatus(http.StatusNotFound) - statusBadGateway = newHTTPErrorStatus(http.StatusBadGateway) -) - -func newHTTPErrorStatus(status int) *httpErrorStatus { - return &httpErrorStatus{ - status: strconv.Itoa(status), - text: []byte(http.StatusText(status)), - } -} - -// StreamHandler handles new stream opened by the edge. The streams can be used to proxy requests or make RPC. -type StreamHandler struct { - // newConfigChan is a send-only channel to notify Supervisor of a new ClientConfig - newConfigChan chan<- *pogs.ClientConfig - // useConfigResultChan is a receive-only channel for Supervisor to communicate the result of applying a new ClientConfig - useConfigResultChan <-chan *pogs.UseConfigurationResult - // originMapper maps tunnel hostname to origin service - tunnelHostnameMapper *tunnelhostnamemapper.TunnelHostnameMapper - logger logger.Service -} - -// NewStreamHandler creates a new StreamHandler -func NewStreamHandler(newConfigChan chan<- *pogs.ClientConfig, - useConfigResultChan <-chan *pogs.UseConfigurationResult, - logger logger.Service, -) *StreamHandler { - return &StreamHandler{ - newConfigChan: newConfigChan, - useConfigResultChan: useConfigResultChan, - tunnelHostnameMapper: tunnelhostnamemapper.NewTunnelHostnameMapper(), - logger: logger, - } -} - -// UseConfiguration implements ClientService -func (s *StreamHandler) UseConfiguration(ctx context.Context, config *pogs.ClientConfig) (*pogs.UseConfigurationResult, error) { - select { - case <-ctx.Done(): - err := fmt.Errorf("Timeout while sending new config to Supervisor") - s.logger.Errorf("streamHandler: %s", err) - return nil, err - case s.newConfigChan <- config: - } - select { - case <-ctx.Done(): - err := fmt.Errorf("Timeout applying new configuration") - s.logger.Errorf("streamHandler: %s", err) - return nil, err - case result := <-s.useConfigResultChan: - return result, nil - } -} - -// UpdateConfig replaces current originmapper mapping with mappings from newConfig -func (s *StreamHandler) UpdateConfig(newConfig []*pogs.ReverseProxyConfig) (failedConfigs []*pogs.FailedConfig) { - - // Delete old configs that aren't in the `newConfig` - toRemove := s.tunnelHostnameMapper.ToRemove(newConfig) - for _, hostnameToRemove := range toRemove { - s.tunnelHostnameMapper.Delete(hostnameToRemove) - } - - // Add new configs that weren't in the old mapper - toAdd := s.tunnelHostnameMapper.ToAdd(newConfig) - for _, tunnelConfig := range toAdd { - tunnelHostname := tunnelConfig.TunnelHostname - originSerice, err := tunnelConfig.OriginConfig.Service(s.logger) - if err != nil { - s.logger.Errorf("streamHandler: tunnelHostname: %s Invalid origin service config: %s", tunnelHostname, err) - failedConfigs = append(failedConfigs, &pogs.FailedConfig{ - Config: tunnelConfig, - Reason: tunnelConfig.FailReason(err), - }) - continue - } - s.tunnelHostnameMapper.Add(tunnelConfig.TunnelHostname, originSerice) - s.logger.Infof("streamHandler: tunnelHostname: %s New origin service config: %v", tunnelHostname, originSerice.Summary()) - } - return -} - -// ServeStream implements MuxedStreamHandler interface -func (s *StreamHandler) ServeStream(stream *h2mux.MuxedStream) error { - if stream.IsRPCStream() { - return s.serveRPC(stream) - } - if err := s.serveRequest(stream); err != nil { - s.logger.Errorf("streamHandler: %s", err) - return err - } - return nil -} - -func (s *StreamHandler) serveRPC(stream *h2mux.MuxedStream) error { - stream.WriteHeaders([]h2mux.Header{{Name: ":status", Value: "200"}}) - main := pogs.ClientService_ServerToClient(s) - rpcConn := rpc.NewConn( - tunnelrpc.NewTransportLogger(s.logger, rpc.StreamTransport(stream)), - rpc.MainInterface(main.Client), - tunnelrpc.ConnLog(s.logger), - ) - return rpcConn.Wait() -} - -func (s *StreamHandler) serveRequest(stream *h2mux.MuxedStream) error { - tunnelHostname := stream.TunnelHostname() - if !tunnelHostname.IsSet() { - s.writeErrorStatus(stream, statusBadRequest) - return fmt.Errorf("stream doesn't have tunnelHostname") - } - - originService, ok := s.tunnelHostnameMapper.Get(tunnelHostname) - if !ok { - s.writeErrorStatus(stream, statusNotFound) - return fmt.Errorf("cannot map tunnel hostname %s to origin", tunnelHostname) - } - - req, err := createRequest(stream, originService.URL()) - if err != nil { - s.writeErrorStatus(stream, statusBadRequest) - return errors.Wrap(err, "cannot create request") - } - - cfRay := s.logRequest(req, tunnelHostname) - s.logger.Debugf("streamHandler: tunnelHostname: %s CF-RAY: %s Request Headers %+v", tunnelHostname, cfRay, req.Header) - - resp, err := originService.Proxy(stream, req) - if err != nil { - s.writeErrorStatus(stream, statusBadGateway) - return errors.Wrap(err, "cannot proxy request") - } - - s.logger.Debugf("streamHandler: tunnelHostname: %s CF-RAY: %s status: %s Response Headers %+v", tunnelHostname, cfRay, resp.Status, resp.Header) - return nil -} - -func (s *StreamHandler) logRequest(req *http.Request, tunnelHostname h2mux.TunnelHostname) string { - cfRay := FindCfRayHeader(req) - lbProbe := IsLBProbeRequest(req) - logger := s.logger - if cfRay != "" { - logger.Debugf("streamHandler: tunnelHostname: %s CF-RAY: %s %s %s %s", tunnelHostname, cfRay, req.Method, req.URL, req.Proto) - } else if lbProbe { - logger.Debugf("streamHandler: tunnelHostname: %s CF-RAY: %s Load Balancer health check %s %s %s", tunnelHostname, cfRay, req.Method, req.URL, req.Proto) - } else { - logger.Infof("streamHandler: tunnelHostname: %s CF-RAY: %s Requests %v does not have CF-RAY header. Please open a support ticket with Cloudflare.", tunnelHostname, cfRay, req) - } - return cfRay -} - -func (s *StreamHandler) writeErrorStatus(stream *h2mux.MuxedStream, status *httpErrorStatus) { - _ = stream.WriteHeaders([]h2mux.Header{ - { - Name: statusPseudoHeader, - Value: status.status, - }, - h2mux.CreateResponseMetaHeader(h2mux.ResponseMetaHeaderField, h2mux.ResponseSourceCloudflared), - }) - _, _ = stream.Write(status.text) -} diff --git a/streamhandler/stream_handler_test.go b/streamhandler/stream_handler_test.go deleted file mode 100644 index 628524b3..00000000 --- a/streamhandler/stream_handler_test.go +++ /dev/null @@ -1,261 +0,0 @@ -package streamhandler - -import ( - "context" - "io" - "net" - "net/http" - "net/http/httptest" - "strconv" - "sync" - "testing" - "time" - - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/logger" - "github.com/cloudflare/cloudflared/tunnelrpc/pogs" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "golang.org/x/sync/errgroup" -) - -const ( - testOpenStreamTimeout = time.Millisecond * 5000 - testHandshakeTimeout = time.Millisecond * 1000 -) - -var ( - testTunnelHostname = h2mux.TunnelHostname("123.cftunnel.com") - baseHeaders = []h2mux.Header{ - {Name: ":method", Value: "GET"}, - {Name: ":scheme", Value: "http"}, - {Name: ":authority", Value: "example.com"}, - {Name: ":path", Value: "/"}, - - // Regular headers must always come after the pseudoheaders - {Name: h2mux.RequestUserHeadersField, Value: ""}, - } - tunnelHostnameHeader = h2mux.Header{Name: h2mux.CloudflaredProxyTunnelHostnameHeader, Value: testTunnelHostname.String()} -) - -func TestServeRequest(t *testing.T) { - l := logger.NewOutputWriter(logger.NewMockWriteManager()) - configChan := make(chan *pogs.ClientConfig) - useConfigResultChan := make(chan *pogs.UseConfigurationResult) - streamHandler := NewStreamHandler(configChan, useConfigResultChan, l) - - message := []byte("Hello cloudflared") - httpServer := httptest.NewServer(&mockHTTPHandler{message}) - - reverseProxyConfigs := []*pogs.ReverseProxyConfig{ - { - TunnelHostname: testTunnelHostname, - OriginConfig: &pogs.HTTPOriginConfig{ - URLString: httpServer.URL, - }, - }, - } - streamHandler.UpdateConfig(reverseProxyConfigs) - - muxPair := NewDefaultMuxerPair(t, streamHandler) - muxPair.Serve(t) - - ctx, cancel := context.WithTimeout(context.Background(), testOpenStreamTimeout) - defer cancel() - - headers := append(baseHeaders, tunnelHostnameHeader) - stream, err := muxPair.EdgeMux.OpenStream(ctx, headers, nil) - assert.NoError(t, err) - assertStatusHeader(t, http.StatusOK, stream.Headers) - assertRespBody(t, message, stream) -} - -func createStreamHandler() *StreamHandler { - configChan := make(chan *pogs.ClientConfig) - useConfigResultChan := make(chan *pogs.UseConfigurationResult) - l := logger.NewOutputWriter(logger.NewMockWriteManager()) - - return NewStreamHandler(configChan, useConfigResultChan, l) -} - -func createRequestMuxPair(t *testing.T, streamHandler *StreamHandler) *DefaultMuxerPair { - muxPair := NewDefaultMuxerPair(t, streamHandler) - muxPair.Serve(t) - - return muxPair -} - -func TestServeStatusBadRequest(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), testOpenStreamTimeout) - defer cancel() - - // No tunnel hostname header, expect to get 400 Bad Request - stream, err := createRequestMuxPair(t, createStreamHandler()).EdgeMux.OpenStream(ctx, baseHeaders, nil) - assert.NoError(t, err) - assertStatusHeader(t, http.StatusBadRequest, stream.Headers) - assertRespBody(t, statusBadRequest.text, stream) -} - -func TestServeInvalidContentLength(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), testOpenStreamTimeout) - defer cancel() - - // Invalid content-length, wouldn't be able to create a request - // Expect to get 400 Bad Request - headers := append(baseHeaders, tunnelHostnameHeader) - headers = append(headers, h2mux.Header{ - Name: "content-length", - Value: "x", - }) - streamHandler := createStreamHandler() - streamHandler.UpdateConfig([]*pogs.ReverseProxyConfig{ - { - TunnelHostname: testTunnelHostname, - OriginConfig: &pogs.HTTPOriginConfig{ - URLString: "", - }, - }, - }) - mux := createRequestMuxPair(t, streamHandler).EdgeMux - stream, err := mux.OpenStream(ctx, headers, nil) - assert.NoError(t, err) - assertStatusHeader(t, http.StatusBadRequest, stream.Headers) - assertRespBody(t, statusBadRequest.text, stream) -} - -func TestServeStatusNotFound(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), testOpenStreamTimeout) - defer cancel() - - // No mapping for the tunnel hostname, expect to get 404 Not Found - headers := append(baseHeaders, tunnelHostnameHeader) - stream, err := createRequestMuxPair(t, createStreamHandler()).EdgeMux.OpenStream(ctx, headers, nil) - assert.NoError(t, err) - assertStatusHeader(t, http.StatusNotFound, stream.Headers) - assertRespBody(t, statusNotFound.text, stream) -} - -func TestServeStatusBadGateway(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), testOpenStreamTimeout) - defer cancel() - - // Nothing listening on empty url, so proxy would fail. Expect to get 502 Bad Gateway - reverseProxyConfigs := []*pogs.ReverseProxyConfig{ - { - TunnelHostname: testTunnelHostname, - OriginConfig: &pogs.HTTPOriginConfig{ - URLString: "", - }, - }, - } - streamHandler := createStreamHandler() - streamHandler.UpdateConfig(reverseProxyConfigs) - headers := append(baseHeaders, tunnelHostnameHeader) - stream, err := createRequestMuxPair(t, streamHandler).EdgeMux.OpenStream(ctx, headers, nil) - assert.NoError(t, err) - assertStatusHeader(t, http.StatusBadGateway, stream.Headers) - assertRespBody(t, statusBadGateway.text, stream) -} - -func assertStatusHeader(t *testing.T, expectedStatus int, headers []h2mux.Header) { - assert.Equal(t, statusPseudoHeader, headers[0].Name) - assert.Equal(t, strconv.Itoa(expectedStatus), headers[0].Value) -} - -func assertRespBody(t *testing.T, expectedRespBody []byte, stream *h2mux.MuxedStream) { - respBody := make([]byte, len(expectedRespBody)) - _, err := stream.Read(respBody) - assert.NoError(t, err) - assert.Equal(t, expectedRespBody, respBody) -} - -type DefaultMuxerPair struct { - OriginMuxConfig h2mux.MuxerConfig - OriginMux *h2mux.Muxer - OriginConn net.Conn - EdgeMuxConfig h2mux.MuxerConfig - EdgeMux *h2mux.Muxer - EdgeConn net.Conn - doneC chan struct{} -} - -func NewDefaultMuxerPair(t *testing.T, h h2mux.MuxedStreamHandler) *DefaultMuxerPair { - origin, edge := net.Pipe() - p := &DefaultMuxerPair{ - OriginMuxConfig: h2mux.MuxerConfig{ - Timeout: testHandshakeTimeout, - Handler: h, - IsClient: true, - Name: "origin", - Logger: logger.NewOutputWriter(logger.NewMockWriteManager()), - DefaultWindowSize: (1 << 8) - 1, - MaxWindowSize: (1 << 15) - 1, - StreamWriteBufferMaxLen: 1024, - }, - OriginConn: origin, - EdgeMuxConfig: h2mux.MuxerConfig{ - Timeout: testHandshakeTimeout, - IsClient: false, - Name: "edge", - Logger: logger.NewOutputWriter(logger.NewMockWriteManager()), - DefaultWindowSize: (1 << 8) - 1, - MaxWindowSize: (1 << 15) - 1, - StreamWriteBufferMaxLen: 1024, - }, - EdgeConn: edge, - doneC: make(chan struct{}), - } - assert.NoError(t, p.Handshake(t.Name())) - return p -} - -func (p *DefaultMuxerPair) Handshake(testName string) error { - ctx, cancel := context.WithTimeout(context.Background(), testHandshakeTimeout) - defer cancel() - errGroup, _ := errgroup.WithContext(ctx) - errGroup.Go(func() (err error) { - p.EdgeMux, err = h2mux.Handshake(p.EdgeConn, p.EdgeConn, p.EdgeMuxConfig, h2mux.NewActiveStreamsMetrics(testName, "edge")) - return errors.Wrap(err, "edge handshake failure") - }) - errGroup.Go(func() (err error) { - p.OriginMux, err = h2mux.Handshake(p.OriginConn, p.OriginConn, p.OriginMuxConfig, h2mux.NewActiveStreamsMetrics(testName, "origin")) - return errors.Wrap(err, "origin handshake failure") - }) - - return errGroup.Wait() -} - -func (p *DefaultMuxerPair) Serve(t assert.TestingT) { - ctx := context.Background() - var wg sync.WaitGroup - wg.Add(2) - go func() { - err := p.EdgeMux.Serve(ctx) - if err != nil && err != io.EOF && err != io.ErrClosedPipe { - t.Errorf("error in edge muxer Serve(): %s", err) - } - p.OriginMux.Shutdown() - wg.Done() - }() - go func() { - err := p.OriginMux.Serve(ctx) - if err != nil && err != io.EOF && err != io.ErrClosedPipe { - t.Errorf("error in origin muxer Serve(): %s", err) - } - p.EdgeMux.Shutdown() - wg.Done() - }() - go func() { - // notify when both muxes have stopped serving - wg.Wait() - close(p.doneC) - }() -} - -type mockHTTPHandler struct { - message []byte -} - -func (mth *mockHTTPHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { - _, _ = w.Write(mth.message) -} diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go deleted file mode 100644 index 8dee085d..00000000 --- a/supervisor/supervisor.go +++ /dev/null @@ -1,205 +0,0 @@ -// Package supervisor is used by declarative tunnels to get/apply new config from the edge. -package supervisor - -import ( - "context" - "crypto/tls" - "fmt" - "os" - "os/signal" - "sync" - "syscall" - - "golang.org/x/sync/errgroup" - - "github.com/cloudflare/cloudflared/cmd/cloudflared/updater" - "github.com/cloudflare/cloudflared/connection" - "github.com/cloudflare/cloudflared/edgediscovery" - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/logger" - "github.com/cloudflare/cloudflared/streamhandler" - "github.com/cloudflare/cloudflared/tunnelrpc/pogs" - "github.com/prometheus/client_golang/prometheus" -) - -type Supervisor struct { - connManager *connection.EdgeManager - streamHandler *streamhandler.StreamHandler - autoupdater *updater.AutoUpdater - supportAutoupdate bool - newConfigChan <-chan *pogs.ClientConfig - useConfigResultChan chan<- *pogs.UseConfigurationResult - state *state - logger logger.Service - metrics metrics -} - -type metrics struct { - configVersion prometheus.Gauge -} - -func newMetrics() metrics { - configVersion := prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: "supervisor", - Subsystem: "supervisor", - Name: "config_version", - Help: "Latest configuration version received from Cloudflare", - }, - ) - prometheus.MustRegister( - configVersion, - ) - return metrics{ - configVersion: configVersion, - } -} - -func NewSupervisor( - defaultClientConfig *pogs.ClientConfig, - userCredential []byte, - tlsConfig *tls.Config, - serviceDiscoverer *edgediscovery.Edge, - cloudflaredConfig *connection.CloudflaredConfig, - autoupdater *updater.AutoUpdater, - supportAutoupdate bool, - logger logger.Service, -) (*Supervisor, error) { - newConfigChan := make(chan *pogs.ClientConfig) - useConfigResultChan := make(chan *pogs.UseConfigurationResult) - streamHandler := streamhandler.NewStreamHandler(newConfigChan, useConfigResultChan, logger) - invalidConfigs := streamHandler.UpdateConfig(defaultClientConfig.ReverseProxyConfigs) - - if len(invalidConfigs) > 0 { - for _, invalidConfig := range invalidConfigs { - logger.Errorf("Tunnel %+v is invalid, reason: %s", invalidConfig.Config, invalidConfig.Reason) - } - return nil, fmt.Errorf("At least 1 Tunnel config is invalid") - } - - tunnelHostnames := make([]h2mux.TunnelHostname, len(defaultClientConfig.ReverseProxyConfigs)) - for i, reverseProxyConfig := range defaultClientConfig.ReverseProxyConfigs { - tunnelHostnames[i] = reverseProxyConfig.TunnelHostname - } - defaultEdgeMgrConfigurable := &connection.EdgeManagerConfigurable{ - TunnelHostnames: tunnelHostnames, - EdgeConnectionConfig: defaultClientConfig.EdgeConnectionConfig, - } - return &Supervisor{ - connManager: connection.NewEdgeManager(streamHandler, defaultEdgeMgrConfigurable, userCredential, tlsConfig, - serviceDiscoverer, cloudflaredConfig, logger), - streamHandler: streamHandler, - autoupdater: autoupdater, - supportAutoupdate: supportAutoupdate, - newConfigChan: newConfigChan, - useConfigResultChan: useConfigResultChan, - state: newState(defaultClientConfig), - logger: logger, - metrics: newMetrics(), - }, nil -} - -func (s *Supervisor) Run(ctx context.Context) error { - errGroup, groupCtx := errgroup.WithContext(ctx) - - errGroup.Go(func() error { - return s.connManager.Run(groupCtx) - }) - - errGroup.Go(func() error { - return s.listenToNewConfig(groupCtx) - }) - - errGroup.Go(func() error { - return s.listenToShutdownSignal(groupCtx) - }) - - if s.supportAutoupdate { - errGroup.Go(func() error { - return s.autoupdater.Run(groupCtx) - }) - } - - err := errGroup.Wait() - s.logger.Errorf("Supervisor terminated, reason: %v", err) - return err -} - -func (s *Supervisor) listenToShutdownSignal(serveCtx context.Context) error { - signals := make(chan os.Signal, 10) - signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) - defer signal.Stop(signals) - - select { - case <-serveCtx.Done(): - return serveCtx.Err() - case sig := <-signals: - return fmt.Errorf("received %v signal", sig) - } -} - -func (s *Supervisor) listenToNewConfig(ctx context.Context) error { - for { - select { - case <-ctx.Done(): - return ctx.Err() - case newConfig := <-s.newConfigChan: - s.useConfigResultChan <- s.notifySubsystemsNewConfig(newConfig) - } - } -} - -func (s *Supervisor) notifySubsystemsNewConfig(newConfig *pogs.ClientConfig) *pogs.UseConfigurationResult { - s.logger.Infof("Received configuration %v", newConfig.Version) - if s.state.hasAppliedVersion(newConfig.Version) { - s.logger.Infof("%v has been applied", newConfig.Version) - return &pogs.UseConfigurationResult{ - Success: true, - } - } - s.metrics.configVersion.Set(float64(newConfig.Version)) - - s.state.updateConfig(newConfig) - var tunnelHostnames []h2mux.TunnelHostname - for _, tunnelConfig := range newConfig.ReverseProxyConfigs { - tunnelHostnames = append(tunnelHostnames, tunnelConfig.TunnelHostname) - } - // Update connManager configurable - s.connManager.UpdateConfigurable(&connection.EdgeManagerConfigurable{ - TunnelHostnames: tunnelHostnames, - EdgeConnectionConfig: newConfig.EdgeConnectionConfig, - }) - // Update streamHandler tunnelHostnameMapper mapping - failedConfigs := s.streamHandler.UpdateConfig(newConfig.ReverseProxyConfigs) - - if s.supportAutoupdate { - s.autoupdater.Update(newConfig.SupervisorConfig.AutoUpdateFrequency) - } - - return &pogs.UseConfigurationResult{ - Success: len(failedConfigs) == 0, - FailedConfigs: failedConfigs, - } -} - -type state struct { - sync.RWMutex - currentConfig *pogs.ClientConfig -} - -func newState(currentConfig *pogs.ClientConfig) *state { - return &state{ - currentConfig: currentConfig, - } -} - -func (s *state) hasAppliedVersion(incomingVersion pogs.Version) bool { - s.RLock() - defer s.RUnlock() - return s.currentConfig.Version.IsNewerOrEqual(incomingVersion) -} - -func (s *state) updateConfig(newConfig *pogs.ClientConfig) { - s.Lock() - defer s.Unlock() - s.currentConfig = newConfig -} diff --git a/tunnelhostnamemapper/tunnelhostnamemapper.go b/tunnelhostnamemapper/tunnelhostnamemapper.go deleted file mode 100644 index 554da985..00000000 --- a/tunnelhostnamemapper/tunnelhostnamemapper.go +++ /dev/null @@ -1,93 +0,0 @@ -package tunnelhostnamemapper - -import ( - "sync" - - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/originservice" - "github.com/cloudflare/cloudflared/tunnelrpc/pogs" -) - -// TunnelHostnameMapper maps TunnelHostname to an OriginService -type TunnelHostnameMapper struct { - sync.RWMutex - tunnelHostnameToOrigin map[h2mux.TunnelHostname]originservice.OriginService -} - -func NewTunnelHostnameMapper() *TunnelHostnameMapper { - return &TunnelHostnameMapper{ - tunnelHostnameToOrigin: make(map[h2mux.TunnelHostname]originservice.OriginService), - } -} - -// Get an OriginService given a TunnelHostname -func (om *TunnelHostnameMapper) Get(key h2mux.TunnelHostname) (originservice.OriginService, bool) { - om.RLock() - defer om.RUnlock() - originService, ok := om.tunnelHostnameToOrigin[key] - return originService, ok -} - -// Add a mapping. If there is already an OriginService with this key, shutdown the old origin service and replace it -// with the new one -func (om *TunnelHostnameMapper) Add(key h2mux.TunnelHostname, os originservice.OriginService) { - om.Lock() - defer om.Unlock() - if oldOS, ok := om.tunnelHostnameToOrigin[key]; ok { - oldOS.Shutdown() - } - om.tunnelHostnameToOrigin[key] = os -} - -// Delete a mapping, and shutdown its OriginService -func (om *TunnelHostnameMapper) Delete(key h2mux.TunnelHostname) (keyFound bool) { - om.Lock() - defer om.Unlock() - if os, ok := om.tunnelHostnameToOrigin[key]; ok { - os.Shutdown() - delete(om.tunnelHostnameToOrigin, key) - return true - } - return false -} - -// ToRemove finds all keys that should be removed from the TunnelHostnameMapper. -func (om *TunnelHostnameMapper) ToRemove(newConfigs []*pogs.ReverseProxyConfig) (toRemove []h2mux.TunnelHostname) { - om.Lock() - defer om.Unlock() - - // Convert into a set, for O(1) lookups instead of O(n) - newConfigSet := toSet(newConfigs) - - // If a config in `om` isn't in `newConfigs`, it must be removed. - for hostname := range om.tunnelHostnameToOrigin { - if _, ok := newConfigSet[hostname]; !ok { - toRemove = append(toRemove, hostname) - } - } - - return -} - -// ToAdd filters the given configs, keeping those that should be added to the TunnelHostnameMapper. -func (om *TunnelHostnameMapper) ToAdd(newConfigs []*pogs.ReverseProxyConfig) (toAdd []*pogs.ReverseProxyConfig) { - om.Lock() - defer om.Unlock() - - // If a config in `newConfigs` isn't in `om`, it must be added. - for _, config := range newConfigs { - if _, ok := om.tunnelHostnameToOrigin[config.TunnelHostname]; !ok { - toAdd = append(toAdd, config) - } - } - - return -} - -func toSet(configs []*pogs.ReverseProxyConfig) map[h2mux.TunnelHostname]*pogs.ReverseProxyConfig { - m := make(map[h2mux.TunnelHostname]*pogs.ReverseProxyConfig) - for _, config := range configs { - m[config.TunnelHostname] = config - } - return m -} diff --git a/tunnelhostnamemapper/tunnelhostnamemapper_test.go b/tunnelhostnamemapper/tunnelhostnamemapper_test.go deleted file mode 100644 index d8f8e655..00000000 --- a/tunnelhostnamemapper/tunnelhostnamemapper_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package tunnelhostnamemapper - -import ( - "fmt" - "net/http" - "net/url" - "reflect" - "sync" - "testing" - "time" - - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/originservice" - "github.com/cloudflare/cloudflared/tunnelrpc/pogs" - "github.com/stretchr/testify/assert" -) - -const ( - routines = 1000 -) - -func TestTunnelHostnameMapperConcurrentAccess(t *testing.T) { - thm := NewTunnelHostnameMapper() - - concurrentOps(t, func(i int) { - // om is empty - os, ok := thm.Get(tunnelHostname(i)) - assert.False(t, ok) - assert.Nil(t, os) - }) - - firstURL, err := url.Parse("https://127.0.0.1:8080") - assert.NoError(t, err) - httpOS := originservice.NewHTTPService(http.DefaultTransport, firstURL, false) - concurrentOps(t, func(i int) { - thm.Add(tunnelHostname(i), httpOS) - }) - - concurrentOps(t, func(i int) { - os, ok := thm.Get(tunnelHostname(i)) - assert.True(t, ok) - assert.Equal(t, httpOS, os) - }) - - secondURL, err := url.Parse("https://127.0.0.1:8080") - assert.NoError(t, err) - secondHTTPOS := originservice.NewHTTPService(http.DefaultTransport, secondURL, true) - concurrentOps(t, func(i int) { - // Add should httpOS with secondHTTPOS - thm.Add(tunnelHostname(i), secondHTTPOS) - }) - - concurrentOps(t, func(i int) { - os, ok := thm.Get(tunnelHostname(i)) - assert.True(t, ok) - assert.Equal(t, secondHTTPOS, os) - }) -} - -func concurrentOps(t *testing.T, f func(i int)) { - var wg sync.WaitGroup - wg.Add(routines) - for i := 0; i < routines; i++ { - go func(i int) { - f(i) - wg.Done() - }(i) - } - wg.Wait() -} - -func tunnelHostname(i int) h2mux.TunnelHostname { - return h2mux.TunnelHostname(fmt.Sprintf("%d.cftunnel.com", i)) -} - -func Test_toSet(t *testing.T) { - - type args struct { - configs []*pogs.ReverseProxyConfig - } - tests := []struct { - name string - args args - want map[h2mux.TunnelHostname]*pogs.ReverseProxyConfig - }{ - { - name: "empty slice should yield empty map", - args: args{}, - want: map[h2mux.TunnelHostname]*pogs.ReverseProxyConfig{}, - }, - { - name: "multiple elements", - args: args{[]*pogs.ReverseProxyConfig{sampleConfig1(), sampleConfig2()}}, - want: map[h2mux.TunnelHostname]*pogs.ReverseProxyConfig{ - "mock.example.com": sampleConfig1(), - "mock2.example.com": sampleConfig2(), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := toSet(tt.args.configs); !reflect.DeepEqual(got, tt.want) { - t.Errorf("toSet() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestTunnelHostnameMapper_ToAdd(t *testing.T) { - type fields struct { - tunnelHostnameToOrigin map[h2mux.TunnelHostname]originservice.OriginService - } - type args struct { - newConfigs []*pogs.ReverseProxyConfig - } - tests := []struct { - name string - fields fields - args args - wantToAdd []*pogs.ReverseProxyConfig - }{ - { - name: "Mapper={}, NewConfig={}, toAdd={}", - }, - { - name: "Mapper={}, NewConfig={x}, toAdd={x}", - args: args{newConfigs: []*pogs.ReverseProxyConfig{sampleConfig1()}}, - wantToAdd: []*pogs.ReverseProxyConfig{sampleConfig1()}, - }, - { - name: "Mapper={x}, NewConfig={x,y}, toAdd={y}", - args: args{newConfigs: []*pogs.ReverseProxyConfig{sampleConfig2()}}, - wantToAdd: []*pogs.ReverseProxyConfig{sampleConfig2()}, - fields: fields{tunnelHostnameToOrigin: map[h2mux.TunnelHostname]originservice.OriginService{ - h2mux.TunnelHostname(sampleConfig1().TunnelHostname): &originservice.HelloWorldService{}, - }}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - om := &TunnelHostnameMapper{ - tunnelHostnameToOrigin: tt.fields.tunnelHostnameToOrigin, - } - if gotToAdd := om.ToAdd(tt.args.newConfigs); !reflect.DeepEqual(gotToAdd, tt.wantToAdd) { - t.Errorf("TunnelHostnameMapper.ToAdd() = %v, want %v", gotToAdd, tt.wantToAdd) - } - }) - } -} - -func TestTunnelHostnameMapper_ToRemove(t *testing.T) { - type fields struct { - tunnelHostnameToOrigin map[h2mux.TunnelHostname]originservice.OriginService - } - type args struct { - newConfigs []*pogs.ReverseProxyConfig - } - tests := []struct { - name string - fields fields - args args - wantToRemove []h2mux.TunnelHostname - }{ - { - name: "Mapper={}, NewConfig={}, toRemove={}", - }, - { - name: "Mapper={x}, NewConfig={}, toRemove={x}", - wantToRemove: []h2mux.TunnelHostname{sampleConfig1().TunnelHostname}, - fields: fields{tunnelHostnameToOrigin: map[h2mux.TunnelHostname]originservice.OriginService{ - h2mux.TunnelHostname(sampleConfig1().TunnelHostname): &originservice.HelloWorldService{}, - }}, - }, - { - name: "Mapper={x}, NewConfig={x}, toRemove={}", - args: args{newConfigs: []*pogs.ReverseProxyConfig{sampleConfig1()}}, - fields: fields{tunnelHostnameToOrigin: map[h2mux.TunnelHostname]originservice.OriginService{ - h2mux.TunnelHostname(sampleConfig1().TunnelHostname): &originservice.HelloWorldService{}, - }}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - om := &TunnelHostnameMapper{ - tunnelHostnameToOrigin: tt.fields.tunnelHostnameToOrigin, - } - if gotToRemove := om.ToRemove(tt.args.newConfigs); !reflect.DeepEqual(gotToRemove, tt.wantToRemove) { - t.Errorf("TunnelHostnameMapper.ToRemove() = %v, want %v", gotToRemove, tt.wantToRemove) - } - }) - } -} - -func sampleConfig1() *pogs.ReverseProxyConfig { - return &pogs.ReverseProxyConfig{ - TunnelHostname: "mock.example.com", - OriginConfig: &pogs.HelloWorldOriginConfig{}, - Retries: 18, - ConnectionTimeout: 5 * time.Second, - CompressionQuality: 3, - } -} - -func sampleConfig2() *pogs.ReverseProxyConfig { - return &pogs.ReverseProxyConfig{ - TunnelHostname: "mock2.example.com", - OriginConfig: &pogs.HelloWorldOriginConfig{}, - Retries: 18, - ConnectionTimeout: 5 * time.Second, - CompressionQuality: 3, - } -} diff --git a/tunnelrpc/pogs/config.go b/tunnelrpc/pogs/config.go deleted file mode 100644 index 6ef537ae..00000000 --- a/tunnelrpc/pogs/config.go +++ /dev/null @@ -1,775 +0,0 @@ -package pogs - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "net" - "net/http" - "net/url" - "time" - - "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/logger" - "github.com/cloudflare/cloudflared/originservice" - "github.com/cloudflare/cloudflared/tlsconfig" - "github.com/cloudflare/cloudflared/tunnelrpc" - - "github.com/pkg/errors" - capnp "zombiezen.com/go/capnproto2" - "zombiezen.com/go/capnproto2/pogs" - "zombiezen.com/go/capnproto2/rpc" - "zombiezen.com/go/capnproto2/server" -) - -/// -/// Structs -/// - -// ClientConfig is a collection of FallibleConfig that determines how cloudflared should function -type ClientConfig struct { - Version Version - SupervisorConfig *SupervisorConfig - EdgeConnectionConfig *EdgeConnectionConfig - DoHProxyConfigs []*DoHProxyConfig `capnp:"dohProxyConfigs"` - ReverseProxyConfigs []*ReverseProxyConfig -} - -func (c *ClientConfig) MarshalBytes() ([]byte, error) { - msg, firstSeg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - if err != nil { - return nil, err - } - capnpEntity, err := tunnelrpc.NewRootClientConfig(firstSeg) - if err != nil { - return nil, err - } - err = MarshalClientConfig(capnpEntity, c) - if err != nil { - return nil, err - } - return msg.Marshal() -} - -func UnmarshalClientConfigFromBytes(clientConfigBytes []byte) (*ClientConfig, error) { - msg, err := capnp.Unmarshal(clientConfigBytes) - if err != nil { - return nil, err - } - capnpClientConfig, err := tunnelrpc.ReadRootClientConfig(msg) - if err != nil { - return nil, err - } - pogsClientConfig, err := UnmarshalClientConfig(capnpClientConfig) - if err != nil { - return nil, err - } - return pogsClientConfig, nil -} - -// Version type models the version of a ClientConfig -type Version uint64 - -func InitVersion() Version { - return Version(0) -} - -func (v Version) IsNewerOrEqual(comparedVersion Version) bool { - return v >= comparedVersion -} - -func (v Version) String() string { - return fmt.Sprintf("Version: %d", v) -} - -// FallibleConfig is an interface implemented by configs that cloudflared might not be able to apply -//go-sumtype:decl FallibleConfig -type FallibleConfig interface { - FailReason(err error) string - isFallibleConfig() -} - -// SupervisorConfig specifies config of components managed by Supervisor other than ConnectionManager -type SupervisorConfig struct { - AutoUpdateFrequency time.Duration - MetricsUpdateFrequency time.Duration - GracePeriod time.Duration -} - -// FailReason impelents FallibleConfig interface for SupervisorConfig -func (sc *SupervisorConfig) FailReason(err error) string { - return fmt.Sprintf("Cannot apply SupervisorConfig, err: %v", err) -} - -func (_ *SupervisorConfig) isFallibleConfig() {} - -// EdgeConnectionConfig specifies what parameters and how may connections should ConnectionManager establish with edge -type EdgeConnectionConfig struct { - NumHAConnections uint8 - HeartbeatInterval time.Duration - Timeout time.Duration - MaxFailedHeartbeats uint64 - UserCredentialPath string -} - -// FailReason impelents FallibleConfig interface for EdgeConnectionConfig -func (cmc *EdgeConnectionConfig) FailReason(err error) string { - return fmt.Sprintf("Cannot apply EdgeConnectionConfig, err: %v", err) -} - -func (_ *EdgeConnectionConfig) isFallibleConfig() {} - -// DoHProxyConfig is configuration for DNS over HTTPS service -type DoHProxyConfig struct { - ListenHost string - ListenPort uint16 - Upstreams []string -} - -// FailReason impelents FallibleConfig interface for DoHProxyConfig -func (dpc *DoHProxyConfig) FailReason(err error) string { - return fmt.Sprintf("Cannot apply DoHProxyConfig, err: %v", err) -} - -func (_ *DoHProxyConfig) isFallibleConfig() {} - -// ReverseProxyConfig how and for what hostnames can this cloudflared proxy -type ReverseProxyConfig struct { - TunnelHostname h2mux.TunnelHostname - OriginConfig OriginConfig - Retries uint64 - ConnectionTimeout time.Duration - CompressionQuality uint64 -} - -func NewReverseProxyConfig( - tunnelHostname string, - originConfig OriginConfig, - retries uint64, - connectionTimeout time.Duration, - compressionQuality uint64, -) (*ReverseProxyConfig, error) { - if originConfig == nil { - return nil, fmt.Errorf("NewReverseProxyConfig: originConfigUnmarshaler was null") - } - return &ReverseProxyConfig{ - TunnelHostname: h2mux.TunnelHostname(tunnelHostname), - OriginConfig: originConfig, - Retries: retries, - ConnectionTimeout: connectionTimeout, - CompressionQuality: compressionQuality, - }, nil -} - -// FailReason impelents FallibleConfig interface for ReverseProxyConfig -func (rpc *ReverseProxyConfig) FailReason(err error) string { - return fmt.Sprintf("Cannot apply ReverseProxyConfig, err: %v", err) -} - -func (_ *ReverseProxyConfig) isFallibleConfig() {} - -//go-sumtype:decl OriginConfig -type OriginConfig interface { - // Service returns a OriginService used to proxy to the origin - Service(logger.Service) (originservice.OriginService, error) - // go-sumtype requires at least one unexported method, otherwise it will complain that interface is not sealed - isOriginConfig() -} - -type HTTPOriginConfig struct { - URLString string `capnp:"urlString"` - TCPKeepAlive time.Duration `capnp:"tcpKeepAlive"` - DialDualStack bool - TLSHandshakeTimeout time.Duration `capnp:"tlsHandshakeTimeout"` - TLSVerify bool `capnp:"tlsVerify"` - OriginCAPool string - OriginServerName string - MaxIdleConnections uint64 - IdleConnectionTimeout time.Duration - ProxyConnectionTimeout time.Duration - ExpectContinueTimeout time.Duration - ChunkedEncoding bool -} - -func (hc *HTTPOriginConfig) Service(logger logger.Service) (originservice.OriginService, error) { - rootCAs, err := tlsconfig.LoadCustomOriginCA(hc.OriginCAPool) - if err != nil { - return nil, err - } - - dialer := &net.Dialer{ - Timeout: hc.ProxyConnectionTimeout, - KeepAlive: hc.TCPKeepAlive, - } - if !hc.DialDualStack { - dialer.FallbackDelay = -1 - } - dialContext := dialer.DialContext - transport := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: dialContext, - TLSClientConfig: &tls.Config{ - RootCAs: rootCAs, - ServerName: hc.OriginServerName, - InsecureSkipVerify: hc.TLSVerify, - }, - TLSHandshakeTimeout: hc.TLSHandshakeTimeout, - MaxIdleConns: int(hc.MaxIdleConnections), - IdleConnTimeout: hc.IdleConnectionTimeout, - ExpectContinueTimeout: hc.ExpectContinueTimeout, - } - url, err := url.Parse(hc.URLString) - if err != nil { - return nil, errors.Wrapf(err, "%s is not a valid URL", hc.URLString) - } - if url.Scheme == "unix" { - transport.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { - return dialContext(ctx, "unix", url.Host) - } - } - return originservice.NewHTTPService(transport, url, hc.ChunkedEncoding), nil -} - -func (*HTTPOriginConfig) isOriginConfig() {} - -type WebSocketOriginConfig struct { - URLString string `capnp:"urlString"` - TLSVerify bool `capnp:"tlsVerify"` - OriginCAPool string - OriginServerName string -} - -func (wsc *WebSocketOriginConfig) Service(logger logger.Service) (originservice.OriginService, error) { - rootCAs, err := tlsconfig.LoadCustomOriginCA(wsc.OriginCAPool) - if err != nil { - return nil, err - } - tlsConfig := &tls.Config{ - RootCAs: rootCAs, - ServerName: wsc.OriginServerName, - InsecureSkipVerify: wsc.TLSVerify, - } - - url, err := url.Parse(wsc.URLString) - if err != nil { - return nil, errors.Wrapf(err, "%s is not a valid URL", wsc.URLString) - } - return originservice.NewWebSocketService(tlsConfig, url, logger) -} - -func (*WebSocketOriginConfig) isOriginConfig() {} - -type HelloWorldOriginConfig struct{} - -func (*HelloWorldOriginConfig) Service(logger logger.Service) (originservice.OriginService, error) { - helloCert, err := tlsconfig.GetHelloCertificateX509() - if err != nil { - return nil, errors.Wrap(err, "Cannot get Hello World server certificate") - } - rootCAs := x509.NewCertPool() - rootCAs.AddCert(helloCert) - transport := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).DialContext, - TLSClientConfig: &tls.Config{ - RootCAs: rootCAs, - }, - MaxIdleConns: 100, - IdleConnTimeout: 90 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - } - return originservice.NewHelloWorldService(transport, logger) -} - -func (*HelloWorldOriginConfig) isOriginConfig() {} - -/* - * Boilerplate to convert between these structs and the primitive structs - * generated by capnp-go. - * Mnemonics for variable names in this section: - * - `p` is for POGS (plain old Go struct) - * - `s` (and `ss`) is for "capnp.Struct", which is the fundamental type - * underlying the capnp-go data structures. - */ - -func MarshalClientConfig(s tunnelrpc.ClientConfig, p *ClientConfig) error { - s.SetVersion(uint64(p.Version)) - - supervisorConfig, err := s.NewSupervisorConfig() - if err != nil { - return errors.Wrap(err, "failed to get SupervisorConfig") - } - if err = MarshalSupervisorConfig(supervisorConfig, p.SupervisorConfig); err != nil { - return errors.Wrap(err, "MarshalSupervisorConfig error") - } - - edgeConnectionConfig, err := s.NewEdgeConnectionConfig() - if err != nil { - return errors.Wrap(err, "failed to get EdgeConnectionConfig") - } - if err := MarshalEdgeConnectionConfig(edgeConnectionConfig, p.EdgeConnectionConfig); err != nil { - return errors.Wrap(err, "MarshalEdgeConnectionConfig error") - } - - if err := marshalDoHProxyConfigs(s, p.DoHProxyConfigs); err != nil { - return errors.Wrap(err, "marshalDoHProxyConfigs error") - } - if err := marshalReverseProxyConfigs(s, p.ReverseProxyConfigs); err != nil { - return errors.Wrap(err, "marshalReverseProxyConfigs error") - } - return nil -} - -func MarshalSupervisorConfig(s tunnelrpc.SupervisorConfig, p *SupervisorConfig) error { - if err := pogs.Insert(tunnelrpc.SupervisorConfig_TypeID, s.Struct, p); err != nil { - return errors.Wrap(err, "failed to insert SupervisorConfig") - } - return nil -} - -func MarshalEdgeConnectionConfig(s tunnelrpc.EdgeConnectionConfig, p *EdgeConnectionConfig) error { - if err := pogs.Insert(tunnelrpc.EdgeConnectionConfig_TypeID, s.Struct, p); err != nil { - return errors.Wrap(err, "failed to insert EdgeConnectionConfig") - } - return nil -} - -func marshalDoHProxyConfigs(s tunnelrpc.ClientConfig, dohProxyConfigs []*DoHProxyConfig) error { - capnpList, err := s.NewDohProxyConfigs(int32(len(dohProxyConfigs))) - if err != nil { - return err - } - for i, unmarshalledConfig := range dohProxyConfigs { - err := MarshalDoHProxyConfig(capnpList.At(i), unmarshalledConfig) - if err != nil { - return err - } - } - return nil -} - -func marshalReverseProxyConfigs(s tunnelrpc.ClientConfig, reverseProxyConfigs []*ReverseProxyConfig) error { - capnpList, err := s.NewReverseProxyConfigs(int32(len(reverseProxyConfigs))) - if err != nil { - return err - } - for i, unmarshalledConfig := range reverseProxyConfigs { - err := MarshalReverseProxyConfig(capnpList.At(i), unmarshalledConfig) - if err != nil { - return err - } - } - return nil -} - -func UnmarshalClientConfig(s tunnelrpc.ClientConfig) (*ClientConfig, error) { - p := new(ClientConfig) - p.Version = Version(s.Version()) - - supervisorConfig, err := s.SupervisorConfig() - if err != nil { - return nil, errors.Wrap(err, "failed to get SupervisorConfig") - } - p.SupervisorConfig, err = UnmarshalSupervisorConfig(supervisorConfig) - if err != nil { - return nil, errors.Wrap(err, "UnmarshalSupervisorConfig error") - } - - edgeConnectionConfig, err := s.EdgeConnectionConfig() - if err != nil { - return nil, errors.Wrap(err, "failed to get ConnectionManagerConfig") - } - p.EdgeConnectionConfig, err = UnmarshalEdgeConnectionConfig(edgeConnectionConfig) - if err != nil { - return nil, errors.Wrap(err, "UnmarshalConnectionManagerConfig error") - } - - p.DoHProxyConfigs, err = unmarshalDoHProxyConfigs(s) - if err != nil { - return nil, errors.Wrap(err, "unmarshalDoHProxyConfigs error") - } - - p.ReverseProxyConfigs, err = unmarshalReverseProxyConfigs(s) - if err != nil { - return nil, errors.Wrap(err, "unmarshalReverseProxyConfigs error") - } - - return p, nil -} - -func UnmarshalSupervisorConfig(s tunnelrpc.SupervisorConfig) (*SupervisorConfig, error) { - p := new(SupervisorConfig) - err := pogs.Extract(p, tunnelrpc.SupervisorConfig_TypeID, s.Struct) - return p, err -} - -func UnmarshalEdgeConnectionConfig(s tunnelrpc.EdgeConnectionConfig) (*EdgeConnectionConfig, error) { - p := new(EdgeConnectionConfig) - err := pogs.Extract(p, tunnelrpc.EdgeConnectionConfig_TypeID, s.Struct) - return p, err -} - -func unmarshalDoHProxyConfigs(s tunnelrpc.ClientConfig) ([]*DoHProxyConfig, error) { - var result []*DoHProxyConfig - marshalledDoHProxyConfigs, err := s.DohProxyConfigs() - if err != nil { - return nil, err - } - for i := 0; i < marshalledDoHProxyConfigs.Len(); i++ { - ss := marshalledDoHProxyConfigs.At(i) - dohProxyConfig, err := UnmarshalDoHProxyConfig(ss) - if err != nil { - return nil, err - } - result = append(result, dohProxyConfig) - } - return result, nil -} - -func unmarshalReverseProxyConfigs(s tunnelrpc.ClientConfig) ([]*ReverseProxyConfig, error) { - var result []*ReverseProxyConfig - marshalledReverseProxyConfigs, err := s.ReverseProxyConfigs() - if err != nil { - return nil, err - } - for i := 0; i < marshalledReverseProxyConfigs.Len(); i++ { - ss := marshalledReverseProxyConfigs.At(i) - reverseProxyConfig, err := UnmarshalReverseProxyConfig(ss) - if err != nil { - return nil, err - } - result = append(result, reverseProxyConfig) - } - return result, nil -} - -func MarshalUseConfigurationResult(s tunnelrpc.UseConfigurationResult, p *UseConfigurationResult) error { - capnpList, err := s.NewFailedConfigs(int32(len(p.FailedConfigs))) - if err != nil { - return errors.Wrap(err, "Cannot create new FailedConfigs") - } - for i, unmarshalledFailedConfig := range p.FailedConfigs { - err := MarshalFailedConfig(capnpList.At(i), unmarshalledFailedConfig) - if err != nil { - return errors.Wrapf(err, "Cannot MarshalFailedConfig at index %d", i) - } - } - s.SetSuccess(p.Success) - return nil -} - -func UnmarshalUseConfigurationResult(s tunnelrpc.UseConfigurationResult) (*UseConfigurationResult, error) { - p := new(UseConfigurationResult) - var failedConfigs []*FailedConfig - marshalledFailedConfigs, err := s.FailedConfigs() - if err != nil { - return nil, errors.Wrap(err, "Cannot get FailedConfigs") - } - for i := 0; i < marshalledFailedConfigs.Len(); i++ { - ss := marshalledFailedConfigs.At(i) - failedConfig, err := UnmarshalFailedConfig(ss) - if err != nil { - return nil, errors.Wrapf(err, "Cannot UnmarshalFailedConfig at index %d", i) - } - failedConfigs = append(failedConfigs, failedConfig) - } - p.FailedConfigs = failedConfigs - p.Success = s.Success() - return p, nil -} - -func MarshalDoHProxyConfig(s tunnelrpc.DoHProxyConfig, p *DoHProxyConfig) error { - return pogs.Insert(tunnelrpc.DoHProxyConfig_TypeID, s.Struct, p) -} - -func UnmarshalDoHProxyConfig(s tunnelrpc.DoHProxyConfig) (*DoHProxyConfig, error) { - p := new(DoHProxyConfig) - err := pogs.Extract(p, tunnelrpc.DoHProxyConfig_TypeID, s.Struct) - return p, err -} - -func MarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig, p *ReverseProxyConfig) error { - s.SetTunnelHostname(p.TunnelHostname.String()) - switch config := p.OriginConfig.(type) { - case *HTTPOriginConfig: - ss, err := s.OriginConfig().NewHttp() - if err != nil { - return err - } - if err := MarshalHTTPOriginConfig(ss, config); err != nil { - return err - } - case *WebSocketOriginConfig: - ss, err := s.OriginConfig().NewWebsocket() - if err != nil { - return err - } - if err := MarshalWebSocketOriginConfig(ss, config); err != nil { - return err - } - case *HelloWorldOriginConfig: - ss, err := s.OriginConfig().NewHelloWorld() - if err != nil { - return err - } - if err := MarshalHelloWorldOriginConfig(ss, config); err != nil { - return err - } - default: - return fmt.Errorf("Unknown type for config: %T", config) - } - s.SetRetries(p.Retries) - s.SetConnectionTimeout(p.ConnectionTimeout.Nanoseconds()) - s.SetCompressionQuality(p.CompressionQuality) - return nil -} - -func UnmarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig) (*ReverseProxyConfig, error) { - p := new(ReverseProxyConfig) - tunnelHostname, err := s.TunnelHostname() - if err != nil { - return nil, err - } - p.TunnelHostname = h2mux.TunnelHostname(tunnelHostname) - switch s.OriginConfig().Which() { - case tunnelrpc.ReverseProxyConfig_originConfig_Which_http: - ss, err := s.OriginConfig().Http() - if err != nil { - return nil, err - } - config, err := UnmarshalHTTPOriginConfig(ss) - if err != nil { - return nil, err - } - p.OriginConfig = config - case tunnelrpc.ReverseProxyConfig_originConfig_Which_websocket: - ss, err := s.OriginConfig().Websocket() - if err != nil { - return nil, err - } - config, err := UnmarshalWebSocketOriginConfig(ss) - if err != nil { - return nil, err - } - p.OriginConfig = config - case tunnelrpc.ReverseProxyConfig_originConfig_Which_helloWorld: - ss, err := s.OriginConfig().HelloWorld() - if err != nil { - return nil, err - } - config, err := UnmarshalHelloWorldOriginConfig(ss) - if err != nil { - return nil, err - } - p.OriginConfig = config - } - p.Retries = s.Retries() - p.ConnectionTimeout = time.Duration(s.ConnectionTimeout()) - p.CompressionQuality = s.CompressionQuality() - return p, nil -} - -func MarshalHTTPOriginConfig(s tunnelrpc.HTTPOriginConfig, p *HTTPOriginConfig) error { - return pogs.Insert(tunnelrpc.HTTPOriginConfig_TypeID, s.Struct, p) -} - -func UnmarshalHTTPOriginConfig(s tunnelrpc.HTTPOriginConfig) (*HTTPOriginConfig, error) { - p := new(HTTPOriginConfig) - err := pogs.Extract(p, tunnelrpc.HTTPOriginConfig_TypeID, s.Struct) - return p, err -} - -func MarshalWebSocketOriginConfig(s tunnelrpc.WebSocketOriginConfig, p *WebSocketOriginConfig) error { - return pogs.Insert(tunnelrpc.WebSocketOriginConfig_TypeID, s.Struct, p) -} - -func UnmarshalWebSocketOriginConfig(s tunnelrpc.WebSocketOriginConfig) (*WebSocketOriginConfig, error) { - p := new(WebSocketOriginConfig) - err := pogs.Extract(p, tunnelrpc.WebSocketOriginConfig_TypeID, s.Struct) - return p, err -} - -func MarshalHelloWorldOriginConfig(s tunnelrpc.HelloWorldOriginConfig, p *HelloWorldOriginConfig) error { - return pogs.Insert(tunnelrpc.HelloWorldOriginConfig_TypeID, s.Struct, p) -} - -func UnmarshalHelloWorldOriginConfig(s tunnelrpc.HelloWorldOriginConfig) (*HelloWorldOriginConfig, error) { - p := new(HelloWorldOriginConfig) - err := pogs.Extract(p, tunnelrpc.HelloWorldOriginConfig_TypeID, s.Struct) - return p, err -} - -type ClientService interface { - UseConfiguration(ctx context.Context, config *ClientConfig) (*UseConfigurationResult, error) -} - -type ClientService_PogsClient struct { - Client capnp.Client - Conn *rpc.Conn -} - -func (c *ClientService_PogsClient) Close() error { - return c.Conn.Close() -} - -func (c *ClientService_PogsClient) UseConfiguration( - ctx context.Context, - config *ClientConfig, -) (*UseConfigurationResult, error) { - client := tunnelrpc.ClientService{Client: c.Client} - promise := client.UseConfiguration(ctx, func(p tunnelrpc.ClientService_useConfiguration_Params) error { - clientServiceConfig, err := p.NewClientServiceConfig() - if err != nil { - return err - } - return MarshalClientConfig(clientServiceConfig, config) - }) - retval, err := promise.Result().Struct() - if err != nil { - return nil, err - } - return UnmarshalUseConfigurationResult(retval) -} - -func ClientService_ServerToClient(s ClientService) tunnelrpc.ClientService { - return tunnelrpc.ClientService_ServerToClient(ClientService_PogsImpl{s}) -} - -type ClientService_PogsImpl struct { - impl ClientService -} - -func (i ClientService_PogsImpl) UseConfiguration(p tunnelrpc.ClientService_useConfiguration) error { - config, err := p.Params.ClientServiceConfig() - if err != nil { - return errors.Wrap(err, "Cannot get CloudflaredConfig parameter") - } - pogsConfig, err := UnmarshalClientConfig(config) - if err != nil { - return errors.Wrap(err, "Cannot unmarshal tunnelrpc.CloudflaredConfig to *CloudflaredConfig") - } - server.Ack(p.Options) - userConfigResult, err := i.impl.UseConfiguration(p.Ctx, pogsConfig) - if err != nil { - return err - } - result, err := p.Results.NewResult() - if err != nil { - return err - } - return MarshalUseConfigurationResult(result, userConfigResult) -} - -type UseConfigurationResult struct { - Success bool - FailedConfigs []*FailedConfig -} - -type FailedConfig struct { - Config FallibleConfig - Reason string -} - -func MarshalFailedConfig(s tunnelrpc.FailedConfig, p *FailedConfig) error { - switch config := p.Config.(type) { - case *SupervisorConfig: - ss, err := s.Config().NewSupervisor() - if err != nil { - return err - } - err = MarshalSupervisorConfig(ss, config) - if err != nil { - return err - } - case *EdgeConnectionConfig: - ss, err := s.Config().NewEdgeConnection() - if err != nil { - return err - } - err = MarshalEdgeConnectionConfig(ss, config) - if err != nil { - return err - } - case *DoHProxyConfig: - ss, err := s.Config().NewDoh() - if err != nil { - return err - } - err = MarshalDoHProxyConfig(ss, config) - if err != nil { - return err - } - case *ReverseProxyConfig: - ss, err := s.Config().NewReverseProxy() - if err != nil { - return err - } - err = MarshalReverseProxyConfig(ss, config) - if err != nil { - return err - } - default: - return fmt.Errorf("Unknown type for Config: %T", config) - } - s.SetReason(p.Reason) - return nil -} - -func UnmarshalFailedConfig(s tunnelrpc.FailedConfig) (*FailedConfig, error) { - p := new(FailedConfig) - switch s.Config().Which() { - case tunnelrpc.FailedConfig_config_Which_supervisor: - ss, err := s.Config().Supervisor() - if err != nil { - return nil, errors.Wrap(err, "Cannot get SupervisorConfig from Config") - } - config, err := UnmarshalSupervisorConfig(ss) - if err != nil { - return nil, errors.Wrap(err, "Cannot UnmarshalSupervisorConfig") - } - p.Config = config - case tunnelrpc.FailedConfig_config_Which_edgeConnection: - ss, err := s.Config().EdgeConnection() - if err != nil { - return nil, errors.Wrap(err, "Cannot get ConnectionManager from Config") - } - config, err := UnmarshalEdgeConnectionConfig(ss) - if err != nil { - return nil, errors.Wrap(err, "Cannot UnmarshalConnectionManagerConfig") - } - p.Config = config - case tunnelrpc.FailedConfig_config_Which_doh: - ss, err := s.Config().Doh() - if err != nil { - return nil, errors.Wrap(err, "Cannot get Doh from Config") - } - config, err := UnmarshalDoHProxyConfig(ss) - if err != nil { - return nil, errors.Wrap(err, "Cannot UnmarshalDoHProxyConfig") - } - p.Config = config - case tunnelrpc.FailedConfig_config_Which_reverseProxy: - ss, err := s.Config().ReverseProxy() - if err != nil { - return nil, errors.Wrap(err, "Cannot get ReverseProxy from Config") - } - config, err := UnmarshalReverseProxyConfig(ss) - if err != nil { - return nil, errors.Wrap(err, "Cannot UnmarshalReverseProxyConfig") - } - p.Config = config - default: - return nil, fmt.Errorf("Unknown type for FailedConfig: %v", s.Config().Which()) - } - reason, err := s.Reason() - if err != nil { - return nil, errors.Wrap(err, "Cannot get Reason") - } - p.Reason = reason - return p, nil -} diff --git a/tunnelrpc/pogs/config_test.go b/tunnelrpc/pogs/config_test.go deleted file mode 100644 index 199a3d47..00000000 --- a/tunnelrpc/pogs/config_test.go +++ /dev/null @@ -1,455 +0,0 @@ -package pogs - -import ( - "fmt" - "reflect" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/cloudflare/cloudflared/logger" - "github.com/cloudflare/cloudflared/tunnelrpc" - capnp "zombiezen.com/go/capnproto2" -) - -// Assert *HTTPOriginConfig implements OriginConfig -var _ OriginConfig = (*HTTPOriginConfig)(nil) - -// Assert *WebSocketOriginConfig implements OriginConfig -var _ OriginConfig = (*WebSocketOriginConfig)(nil) - -// Assert *HelloWorldOriginConfig implements OriginConfig -var _ OriginConfig = (*HelloWorldOriginConfig)(nil) - -func TestVersion(t *testing.T) { - firstVersion := InitVersion() - secondVersion := Version(1) - assert.False(t, firstVersion.IsNewerOrEqual(secondVersion)) - assert.True(t, secondVersion.IsNewerOrEqual(firstVersion)) - assert.True(t, secondVersion.IsNewerOrEqual(secondVersion)) -} - -func TestClientConfigCapnp(t *testing.T) { - for i, testCase := range ClientConfigTestCases() { - _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - capnpEntity, err := tunnelrpc.NewClientConfig(seg) - if !assert.NoError(t, err) { - t.Fatal("Couldn't initialize a new message") - } - err = MarshalClientConfig(capnpEntity, testCase) - if !assert.NoError(t, err, "testCase index %v failed to marshal", i) { - continue - } - result, err := UnmarshalClientConfig(capnpEntity) - if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) { - continue - } - assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) - } -} - -func ClientConfigTestCases() []*ClientConfig { - - addDoHProxyConfigs := func(c *ClientConfig) { - c.DoHProxyConfigs = []*DoHProxyConfig{ - sampleDoHProxyConfig(), - } - } - addReverseProxyConfigs := func(c *ClientConfig) { - c.ReverseProxyConfigs = []*ReverseProxyConfig{ - sampleReverseProxyConfig(), - sampleReverseProxyConfig(func(c *ReverseProxyConfig) { - }), - sampleReverseProxyConfig(func(c *ReverseProxyConfig) { - c.OriginConfig = sampleHTTPOriginConfig() - }), - sampleReverseProxyConfig(func(c *ReverseProxyConfig) { - c.OriginConfig = sampleHTTPOriginConfigUnixPath() - }), - sampleReverseProxyConfig(func(c *ReverseProxyConfig) { - c.OriginConfig = sampleWebSocketOriginConfig() - }), - } - } - - testCases := []*ClientConfig{ - sampleClientConfig(), - sampleClientConfig(addDoHProxyConfigs), - sampleClientConfig(addReverseProxyConfigs), - sampleClientConfig(addDoHProxyConfigs, addReverseProxyConfigs), - } - - return testCases -} - -func TestClientConfig(t *testing.T) { - for _, testCase := range ClientConfigTestCases() { - b, err := testCase.MarshalBytes() - assert.NoError(t, err) - - clientConfig, err := UnmarshalClientConfigFromBytes(b) - assert.NoError(t, err) - - assert.Equal(t, testCase, clientConfig) - } -} - -func TestUseConfigurationResult(t *testing.T) { - testCases := []*UseConfigurationResult{ - &UseConfigurationResult{ - Success: true, - }, - &UseConfigurationResult{ - Success: false, - FailedConfigs: []*FailedConfig{ - { - Config: sampleReverseProxyConfig(), - Reason: "Invalid certificate", - }, - { - Config: sampleDoHProxyConfig(), - Reason: "Cannot listen on port 53", - }, - }, - }, - } - for i, testCase := range testCases { - _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - capnpEntity, err := tunnelrpc.NewUseConfigurationResult(seg) - if !assert.NoError(t, err) { - t.Fatal("Couldn't initialize a new message") - } - err = MarshalUseConfigurationResult(capnpEntity, testCase) - if !assert.NoError(t, err, "testCase index %v failed to marshal", i) { - continue - } - result, err := UnmarshalUseConfigurationResult(capnpEntity) - if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) { - continue - } - assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) - } -} - -func TestDoHProxyConfig(t *testing.T) { - testCases := []*DoHProxyConfig{ - sampleDoHProxyConfig(), - sampleDoHProxyConfig(func(c *DoHProxyConfig) { - c.Upstreams = nil - }), - } - for i, testCase := range testCases { - _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - capnpEntity, err := tunnelrpc.NewDoHProxyConfig(seg) - if !assert.NoError(t, err) { - t.Fatal("Couldn't initialize a new message") - } - err = MarshalDoHProxyConfig(capnpEntity, testCase) - if !assert.NoError(t, err, "testCase index %v failed to marshal", i) { - continue - } - result, err := UnmarshalDoHProxyConfig(capnpEntity) - if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) { - continue - } - assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) - } -} - -func TestReverseProxyConfig(t *testing.T) { - testCases := []*ReverseProxyConfig{ - sampleReverseProxyConfig(), - sampleReverseProxyConfig(func(c *ReverseProxyConfig) { - c.OriginConfig = sampleHTTPOriginConfig() - }), - sampleReverseProxyConfig(func(c *ReverseProxyConfig) { - c.OriginConfig = sampleHTTPOriginConfigUnixPath() - }), - sampleReverseProxyConfig(func(c *ReverseProxyConfig) { - c.OriginConfig = sampleWebSocketOriginConfig() - }), - } - for i, testCase := range testCases { - _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - capnpEntity, err := tunnelrpc.NewReverseProxyConfig(seg) - if !assert.NoError(t, err) { - t.Fatal("Couldn't initialize a new message") - } - err = MarshalReverseProxyConfig(capnpEntity, testCase) - if !assert.NoError(t, err, "testCase index %v failed to marshal", i) { - continue - } - result, err := UnmarshalReverseProxyConfig(capnpEntity) - if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) { - continue - } - assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) - } -} - -func TestHTTPOriginConfig(t *testing.T) { - testCases := []*HTTPOriginConfig{ - sampleHTTPOriginConfig(), - } - for i, testCase := range testCases { - _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - capnpEntity, err := tunnelrpc.NewHTTPOriginConfig(seg) - if !assert.NoError(t, err) { - t.Fatal("Couldn't initialize a new message") - } - err = MarshalHTTPOriginConfig(capnpEntity, testCase) - if !assert.NoError(t, err, "testCase index %v failed to marshal", i) { - continue - } - result, err := UnmarshalHTTPOriginConfig(capnpEntity) - if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) { - continue - } - assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) - } -} - -func TestWebSocketOriginConfig(t *testing.T) { - testCases := []*WebSocketOriginConfig{ - sampleWebSocketOriginConfig(), - } - for i, testCase := range testCases { - _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - capnpEntity, err := tunnelrpc.NewWebSocketOriginConfig(seg) - if !assert.NoError(t, err) { - t.Fatal("Couldn't initialize a new message") - } - err = MarshalWebSocketOriginConfig(capnpEntity, testCase) - if !assert.NoError(t, err, "testCase index %v failed to marshal", i) { - continue - } - result, err := UnmarshalWebSocketOriginConfig(capnpEntity) - if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) { - continue - } - assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) - } -} - -func TestOriginConfigInvalidURL(t *testing.T) { - invalidConfigs := []OriginConfig{ - &HTTPOriginConfig{ - // this url doesn't have a scheme - URLString: "127.0.0.1:36192", - }, - &WebSocketOriginConfig{ - URLString: "127.0.0.1:36192", - }, - } - logger := logger.NewOutputWriter(logger.NewMockWriteManager()) - - for _, config := range invalidConfigs { - service, err := config.Service(logger) - assert.Error(t, err) - assert.Nil(t, service) - } -} - -////////////////////////////////////////////////////////////////////////////// -// Functions to generate sample data for ease of testing -// -// There's one "sample" function per struct type. Each goes like this: -// 1. Initialize an instance of the relevant struct. -// 2. Ensure the instance has no zero-valued fields. (This catches the -// error-case where a field was added, but we forgot to add code to -// marshal/unmarshal this field in CapnProto.) -// 3. Apply one or more "override" functions (which accept a -// pointer-to-struct, so they can mutate the instance). - -// sampleClientConfig initializes a new ClientConfig literal, -// applies any number of overrides to it, and returns it. -func sampleClientConfig(overrides ...func(*ClientConfig)) *ClientConfig { - sample := &ClientConfig{ - Version: Version(1337), - SupervisorConfig: sampleSupervisorConfig(), - EdgeConnectionConfig: sampleEdgeConnectionConfig(), - } - sample.ensureNoZeroFields() - for _, f := range overrides { - f(sample) - } - return sample -} - -func sampleSupervisorConfig() *SupervisorConfig { - sample := &SupervisorConfig{ - AutoUpdateFrequency: 21 * time.Hour, - MetricsUpdateFrequency: 11 * time.Minute, - GracePeriod: 31 * time.Second, - } - sample.ensureNoZeroFields() - return sample -} - -func sampleEdgeConnectionConfig() *EdgeConnectionConfig { - sample := &EdgeConnectionConfig{ - NumHAConnections: 49, - HeartbeatInterval: 5 * time.Second, - Timeout: 9 * time.Second, - MaxFailedHeartbeats: 9001, - UserCredentialPath: "/Users/example/.cloudflared/cert.pem", - } - sample.ensureNoZeroFields() - return sample -} - -// sampleDoHProxyConfig initializes a new DoHProxyConfig struct, -// applies any number of overrides to it, and returns it. -func sampleDoHProxyConfig(overrides ...func(*DoHProxyConfig)) *DoHProxyConfig { - sample := &DoHProxyConfig{ - ListenHost: "127.0.0.1", - ListenPort: 53, - Upstreams: []string{"1.1.1.1", "1.0.0.1"}, - } - sample.ensureNoZeroFields() - for _, f := range overrides { - f(sample) - } - return sample -} - -// sampleReverseProxyConfig initializes a new ReverseProxyConfig struct, -// applies any number of overrides to it, and returns it. -func sampleReverseProxyConfig(overrides ...func(*ReverseProxyConfig)) *ReverseProxyConfig { - sample := &ReverseProxyConfig{ - TunnelHostname: "mock-non-lb-tunnel.example.com", - OriginConfig: &HelloWorldOriginConfig{}, - Retries: 18, - ConnectionTimeout: 5 * time.Second, - CompressionQuality: 3, - } - sample.ensureNoZeroFields() - for _, f := range overrides { - f(sample) - } - return sample -} - -func sampleHTTPOriginConfig(overrides ...func(*HTTPOriginConfig)) *HTTPOriginConfig { - sample := &HTTPOriginConfig{ - URLString: "https.example.com", - TCPKeepAlive: 7 * time.Second, - DialDualStack: true, - TLSHandshakeTimeout: 11 * time.Second, - TLSVerify: true, - OriginCAPool: "/etc/cert.pem", - OriginServerName: "secure.example.com", - MaxIdleConnections: 19, - IdleConnectionTimeout: 17 * time.Second, - ProxyConnectionTimeout: 15 * time.Second, - ExpectContinueTimeout: 21 * time.Second, - ChunkedEncoding: true, - } - sample.ensureNoZeroFields() - for _, f := range overrides { - f(sample) - } - return sample -} - -func sampleHTTPOriginConfigUnixPath(overrides ...func(*HTTPOriginConfig)) *HTTPOriginConfig { - sample := &HTTPOriginConfig{ - URLString: "unix:/var/lib/file.sock", - TCPKeepAlive: 7 * time.Second, - DialDualStack: true, - TLSHandshakeTimeout: 11 * time.Second, - TLSVerify: true, - OriginCAPool: "/etc/cert.pem", - OriginServerName: "secure.example.com", - MaxIdleConnections: 19, - IdleConnectionTimeout: 17 * time.Second, - ProxyConnectionTimeout: 15 * time.Second, - ExpectContinueTimeout: 21 * time.Second, - ChunkedEncoding: true, - } - sample.ensureNoZeroFields() - for _, f := range overrides { - f(sample) - } - return sample -} - -func sampleWebSocketOriginConfig(overrides ...func(*WebSocketOriginConfig)) *WebSocketOriginConfig { - sample := &WebSocketOriginConfig{ - URLString: "ssh://example.com", - TLSVerify: true, - OriginCAPool: "/etc/cert.pem", - OriginServerName: "secure.example.com", - } - sample.ensureNoZeroFields() - for _, f := range overrides { - f(sample) - } - return sample -} - -func (c *ClientConfig) ensureNoZeroFields() { - ensureNoZeroFieldsInSample(reflect.ValueOf(c), []string{"DoHProxyConfigs", "ReverseProxyConfigs"}) -} - -func (c *SupervisorConfig) ensureNoZeroFields() { - ensureNoZeroFieldsInSample(reflect.ValueOf(c), []string{}) -} - -func (c *EdgeConnectionConfig) ensureNoZeroFields() { - ensureNoZeroFieldsInSample(reflect.ValueOf(c), []string{}) -} - -func (c *DoHProxyConfig) ensureNoZeroFields() { - ensureNoZeroFieldsInSample(reflect.ValueOf(c), []string{}) -} - -func (c *ReverseProxyConfig) ensureNoZeroFields() { - ensureNoZeroFieldsInSample(reflect.ValueOf(c), []string{}) -} - -func (c *HTTPOriginConfig) ensureNoZeroFields() { - ensureNoZeroFieldsInSample(reflect.ValueOf(c), []string{}) -} - -func (c *WebSocketOriginConfig) ensureNoZeroFields() { - ensureNoZeroFieldsInSample(reflect.ValueOf(c), []string{}) -} - -// ensureNoZeroFieldsInSample checks that all fields in the sample struct, -// except those listed in `allowedZeroFieldNames`, are initialized to nonzero -// values. Note that the value has to be a pointer for reflection to work -// correctly: -// e := &ExampleStruct{ ... } -// ensureNoZeroFieldsInSample(reflect.ValueOf(e), []string{}) -// -// Context: -// Our tests work by taking a sample struct and marshalling/unmarshalling it. -// This makes them easy to write, but introduces some risk: if we don't -// include a field in the sample value, it won't be covered under tests. -// This check reduces that risk by requiring fields to be either initialized -// or explicitly uninitialized. -func ensureNoZeroFieldsInSample(ptrToSampleValue reflect.Value, allowedZeroFieldNames []string) { - sampleValue := ptrToSampleValue.Elem() - structType := ptrToSampleValue.Type().Elem() - - allowedZeroFieldSet := make(map[string]bool) - for _, name := range allowedZeroFieldNames { - if _, ok := structType.FieldByName(name); !ok { - panic(fmt.Sprintf("struct %v has no field %v", structType.Name(), name)) - } - allowedZeroFieldSet[name] = true - } - - for i := 0; i < structType.NumField(); i++ { - if allowedZeroFieldSet[structType.Field(i).Name] { - continue - } - - zeroValue := reflect.Zero(structType.Field(i).Type) - if reflect.DeepEqual(zeroValue.Interface(), sampleValue.Field(i).Interface()) { - panic(fmt.Sprintf("In the sample value for struct %v, field %v was not initialized", structType.Name(), structType.Field(i).Name)) - } - } -} diff --git a/tunnelrpc/pogs/tunnelrpc.go b/tunnelrpc/pogs/tunnelrpc.go index 279c0d09..c12aedaa 100644 --- a/tunnelrpc/pogs/tunnelrpc.go +++ b/tunnelrpc/pogs/tunnelrpc.go @@ -3,11 +3,8 @@ package pogs import ( "context" "fmt" - "time" "github.com/cloudflare/cloudflared/tunnelrpc" - "github.com/google/uuid" - "github.com/pkg/errors" capnp "zombiezen.com/go/capnproto2" "zombiezen.com/go/capnproto2/pogs" @@ -184,143 +181,6 @@ func UnmarshalRegistrationOptions(s tunnelrpc.RegistrationOptions) (*Registratio return p, err } -// ConnectResult models the result of Connect RPC, implemented by ConnectError and ConnectSuccess. -type ConnectResult interface { - ConnectError() *ConnectError - ConnectedTo() string - ClientConfig() *ClientConfig - Marshal(s tunnelrpc.ConnectResult) error -} - -func MarshalConnectResult(s tunnelrpc.ConnectResult, p ConnectResult) error { - return p.Marshal(s) -} - -func UnmarshalConnectResult(s tunnelrpc.ConnectResult) (ConnectResult, error) { - switch s.Result().Which() { - case tunnelrpc.ConnectResult_result_Which_err: - capnpConnectError, err := s.Result().Err() - if err != nil { - return nil, err - } - return UnmarshalConnectError(capnpConnectError) - case tunnelrpc.ConnectResult_result_Which_success: - capnpConnectSuccess, err := s.Result().Success() - if err != nil { - return nil, err - } - return UnmarshalConnectSuccess(capnpConnectSuccess) - default: - return nil, fmt.Errorf("Unmarshal %v not implemented yet", s.Result().Which().String()) - } -} - -// ConnectSuccess is the concrete returned type when Connect RPC succeed -type ConnectSuccess struct { - ServerLocationName string - Config *ClientConfig -} - -func (*ConnectSuccess) ConnectError() *ConnectError { - return nil -} - -func (cs *ConnectSuccess) ConnectedTo() string { - return cs.ServerLocationName -} - -func (cs *ConnectSuccess) ClientConfig() *ClientConfig { - return cs.Config -} - -func (cs *ConnectSuccess) Marshal(s tunnelrpc.ConnectResult) error { - capnpConnectSuccess, err := s.Result().NewSuccess() - if err != nil { - return err - } - - err = capnpConnectSuccess.SetServerLocationName(cs.ServerLocationName) - if err != nil { - return errors.Wrap(err, "failed to set ConnectSuccess.ServerLocationName") - } - - if cs.Config != nil { - capnpClientConfig, err := capnpConnectSuccess.NewClientConfig() - if err != nil { - return errors.Wrap(err, "failed to initialize ConnectSuccess.ClientConfig") - } - if err := MarshalClientConfig(capnpClientConfig, cs.Config); err != nil { - return errors.Wrap(err, "failed to marshal ClientConfig") - } - } - - return nil -} - -func UnmarshalConnectSuccess(s tunnelrpc.ConnectSuccess) (*ConnectSuccess, error) { - p := new(ConnectSuccess) - - serverLocationName, err := s.ServerLocationName() - if err != nil { - return nil, errors.Wrap(err, "failed to get tunnelrpc.ConnectSuccess.ServerLocationName") - } - p.ServerLocationName = serverLocationName - - if s.HasClientConfig() { - capnpClientConfig, err := s.ClientConfig() - if err != nil { - return nil, errors.Wrap(err, "failed to get tunnelrpc.ConnectSuccess.ClientConfig") - } - p.Config, err = UnmarshalClientConfig(capnpClientConfig) - if err != nil { - return nil, errors.Wrap(err, "failed to get unmarshal ClientConfig") - } - } - - return p, nil -} - -// ConnectError is the concrete returned type when Connect RPC encounters some error -type ConnectError struct { - Cause string - RetryAfter time.Duration - ShouldRetry bool -} - -func (ce *ConnectError) ConnectError() *ConnectError { - return ce -} - -func (*ConnectError) ConnectedTo() string { - return "" -} - -func (*ConnectError) ClientConfig() *ClientConfig { - return nil -} - -func (ce *ConnectError) Marshal(s tunnelrpc.ConnectResult) error { - capnpConnectError, err := s.Result().NewErr() - if err != nil { - return err - } - return MarshalConnectError(capnpConnectError, ce) -} - -func MarshalConnectError(s tunnelrpc.ConnectError, p *ConnectError) error { - return pogs.Insert(tunnelrpc.ConnectError_TypeID, s.Struct, p) -} - -func UnmarshalConnectError(s tunnelrpc.ConnectError) (*ConnectError, error) { - p := new(ConnectError) - err := pogs.Extract(p, tunnelrpc.ConnectError_TypeID, s.Struct) - return p, err -} - -func (e *ConnectError) Error() string { - return e.Cause -} - type Tag struct { Name string `json:"name"` Value string `json:"value"` @@ -340,102 +200,10 @@ func UnmarshalServerInfo(s tunnelrpc.ServerInfo) (*ServerInfo, error) { return p, err } -type ConnectParameters struct { - OriginCert []byte - CloudflaredID uuid.UUID - NumPreviousAttempts uint8 - Tags []Tag - CloudflaredVersion string - IntentLabel string -} - -func MarshalConnectParameters(s tunnelrpc.CapnpConnectParameters, p *ConnectParameters) error { - if err := s.SetOriginCert(p.OriginCert); err != nil { - return err - } - cloudflaredIDBytes, err := p.CloudflaredID.MarshalBinary() - if err != nil { - return err - } - if err := s.SetCloudflaredID(cloudflaredIDBytes); err != nil { - return err - } - s.SetNumPreviousAttempts(p.NumPreviousAttempts) - if len(p.Tags) > 0 { - tagsCapnpList, err := s.NewTags(int32(len(p.Tags))) - if err != nil { - return err - } - for i, tag := range p.Tags { - tagCapnp := tagsCapnpList.At(i) - if err := tagCapnp.SetName(tag.Name); err != nil { - return err - } - if err := tagCapnp.SetValue(tag.Value); err != nil { - return err - } - } - } - if err := s.SetCloudflaredVersion(p.CloudflaredVersion); err != nil { - return err - } - return s.SetIntentLabel(p.IntentLabel) -} - -func UnmarshalConnectParameters(s tunnelrpc.CapnpConnectParameters) (*ConnectParameters, error) { - originCert, err := s.OriginCert() - if err != nil { - return nil, err - } - - cloudflaredIDBytes, err := s.CloudflaredID() - if err != nil { - return nil, err - } - cloudflaredID, err := uuid.FromBytes(cloudflaredIDBytes) - if err != nil { - return nil, err - } - - tagsCapnpList, err := s.Tags() - if err != nil { - return nil, err - } - var tags []Tag - for i := 0; i < tagsCapnpList.Len(); i++ { - tagCapnp := tagsCapnpList.At(i) - name, err := tagCapnp.Name() - if err != nil { - return nil, err - } - value, err := tagCapnp.Value() - if err != nil { - return nil, err - } - tags = append(tags, Tag{Name: name, Value: value}) - } - - cloudflaredVersion, err := s.CloudflaredVersion() - if err != nil { - return nil, err - } - - intentLabel, err := s.IntentLabel() - return &ConnectParameters{ - OriginCert: originCert, - CloudflaredID: cloudflaredID, - NumPreviousAttempts: s.NumPreviousAttempts(), - Tags: tags, - CloudflaredVersion: cloudflaredVersion, - IntentLabel: intentLabel, - }, nil -} - type TunnelServer interface { RegisterTunnel(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) *TunnelRegistration GetServerInfo(ctx context.Context) (*ServerInfo, error) UnregisterTunnel(ctx context.Context, gracePeriodNanoSec int64) error - Connect(ctx context.Context, parameters *ConnectParameters) (ConnectResult, error) Authenticate(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) (*AuthenticateResponse, error) ReconnectTunnel(ctx context.Context, jwt, eventDigest, connDigest []byte, hostname string, options *RegistrationOptions) (*TunnelRegistration, error) } @@ -494,25 +262,8 @@ func (i TunnelServer_PogsImpl) UnregisterTunnel(p tunnelrpc.TunnelServer_unregis return i.impl.UnregisterTunnel(p.Ctx, gracePeriodNanoSec) } -func (i TunnelServer_PogsImpl) Connect(p tunnelrpc.TunnelServer_connect) error { - parameters, err := p.Params.Parameters() - if err != nil { - return err - } - pogsParameters, err := UnmarshalConnectParameters(parameters) - if err != nil { - return err - } - server.Ack(p.Options) - connectResult, err := i.impl.Connect(p.Ctx, pogsParameters) - if err != nil { - return err - } - result, err := p.Results.NewResult() - if err != nil { - return err - } - return connectResult.Marshal(result) +func (i TunnelServer_PogsImpl) ObsoleteDeclarativeTunnelConnect(p tunnelrpc.TunnelServer_obsoleteDeclarativeTunnelConnect) error { + return fmt.Errorf("RPC to create declarative tunnel connection has been deprecated") } type TunnelServer_PogsClient struct { @@ -578,25 +329,3 @@ func (c TunnelServer_PogsClient) UnregisterTunnel(ctx context.Context, gracePeri _, err := promise.Struct() return err } - -func (c TunnelServer_PogsClient) Connect(ctx context.Context, - parameters *ConnectParameters, -) (ConnectResult, error) { - client := tunnelrpc.TunnelServer{Client: c.Client} - promise := client.Connect(ctx, func(p tunnelrpc.TunnelServer_connect_Params) error { - connectParameters, err := p.NewParameters() - if err != nil { - return err - } - err = MarshalConnectParameters(connectParameters, parameters) - if err != nil { - return err - } - return nil - }) - retval, err := promise.Result().Struct() - if err != nil { - return nil, err - } - return UnmarshalConnectResult(retval) -} diff --git a/tunnelrpc/pogs/tunnelrpc_test.go b/tunnelrpc/pogs/tunnelrpc_test.go index f28a825e..e133f877 100644 --- a/tunnelrpc/pogs/tunnelrpc_test.go +++ b/tunnelrpc/pogs/tunnelrpc_test.go @@ -2,12 +2,9 @@ package pogs import ( "fmt" - "reflect" "testing" - "time" "github.com/cloudflare/cloudflared/tunnelrpc" - "github.com/google/uuid" "github.com/stretchr/testify/assert" capnp "zombiezen.com/go/capnproto2" ) @@ -56,94 +53,3 @@ func TestTunnelRegistration(t *testing.T) { } } - -func TestConnectResult(t *testing.T) { - testCases := []ConnectResult{ - &ConnectError{ - Cause: "it broke", - ShouldRetry: false, - RetryAfter: 2 * time.Second, - }, - &ConnectSuccess{ - ServerLocationName: "SFO", - Config: sampleClientConfig(), - }, - &ConnectSuccess{ - ServerLocationName: "", - Config: nil, - }, - } - for i, testCase := range testCases { - _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - capnpEntity, err := tunnelrpc.NewConnectResult(seg) - if !assert.NoError(t, err) { - t.Fatal("Couldn't initialize a new message") - } - err = MarshalConnectResult(capnpEntity, testCase) - if !assert.NoError(t, err, "testCase #%v failed to marshal", i) { - continue - } - result, err := UnmarshalConnectResult(capnpEntity) - if !assert.NoError(t, err, "testCase #%v failed to unmarshal", i) { - continue - } - assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) - } -} - -func TestConnectParameters(t *testing.T) { - testCases := []*ConnectParameters{ - sampleConnectParameters(), - sampleConnectParameters(func(c *ConnectParameters) { - c.IntentLabel = "my_intent" - }), - sampleConnectParameters(func(c *ConnectParameters) { - c.Tags = nil - }), - } - for i, testCase := range testCases { - _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) - capnpEntity, err := tunnelrpc.NewCapnpConnectParameters(seg) - if !assert.NoError(t, err) { - t.Fatal("Couldn't initialize a new message") - } - err = MarshalConnectParameters(capnpEntity, testCase) - if !assert.NoError(t, err, "testCase index %v failed to marshal", i) { - continue - } - result, err := UnmarshalConnectParameters(capnpEntity) - if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) { - continue - } - assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) - } -} - -func sampleConnectParameters(overrides ...func(*ConnectParameters)) *ConnectParameters { - cloudflaredID, err := uuid.Parse("ED7BA470-8E54-465E-825C-99712043E01C") - if err != nil { - panic(err) - } - sample := &ConnectParameters{ - OriginCert: []byte("my-origin-cert"), - CloudflaredID: cloudflaredID, - NumPreviousAttempts: 19, - Tags: []Tag{ - Tag{ - Name: "provision-method", - Value: "new", - }, - }, - CloudflaredVersion: "7.0", - IntentLabel: "my_intent", - } - sample.ensureNoZeroFields() - for _, f := range overrides { - f(sample) - } - return sample -} - -func (c *ConnectParameters) ensureNoZeroFields() { - ensureNoZeroFieldsInSample(reflect.ValueOf(c), []string{}) -} diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp index 839fc0e5..cb707400 100644 --- a/tunnelrpc/tunnelrpc.capnp +++ b/tunnelrpc/tunnelrpc.capnp @@ -295,7 +295,8 @@ interface TunnelServer { registerTunnel @0 (originCert :Data, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration); getServerInfo @1 () -> (result :ServerInfo); unregisterTunnel @2 (gracePeriodNanoSec :Int64) -> (); - connect @3 (parameters :CapnpConnectParameters) -> (result :ConnectResult); + # obsoleteDeclarativeTunnelConnect RPC deprecated in TUN-3019 + obsoleteDeclarativeTunnelConnect @3 (parameters :CapnpConnectParameters) -> (result :ConnectResult); authenticate @4 (originCert :Data, hostname :Text, options :RegistrationOptions) -> (result :AuthenticateResponse); reconnectTunnel @5 (jwt :Data, eventDigest :Data, connDigest :Data, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration); } diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go index 775ceff9..5b824580 100644 --- a/tunnelrpc/tunnelrpc.capnp.go +++ b/tunnelrpc/tunnelrpc.capnp.go @@ -2874,9 +2874,9 @@ func (c TunnelServer) UnregisterTunnel(ctx context.Context, params func(TunnelSe } return TunnelServer_unregisterTunnel_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} } -func (c TunnelServer) Connect(ctx context.Context, params func(TunnelServer_connect_Params) error, opts ...capnp.CallOption) TunnelServer_connect_Results_Promise { +func (c TunnelServer) ObsoleteDeclarativeTunnelConnect(ctx context.Context, params func(TunnelServer_obsoleteDeclarativeTunnelConnect_Params) error, opts ...capnp.CallOption) TunnelServer_obsoleteDeclarativeTunnelConnect_Results_Promise { if c.Client == nil { - return TunnelServer_connect_Results_Promise{Pipeline: capnp.NewPipeline(capnp.ErrorAnswer(capnp.ErrNullClient))} + return TunnelServer_obsoleteDeclarativeTunnelConnect_Results_Promise{Pipeline: capnp.NewPipeline(capnp.ErrorAnswer(capnp.ErrNullClient))} } call := &capnp.Call{ Ctx: ctx, @@ -2884,15 +2884,17 @@ func (c TunnelServer) Connect(ctx context.Context, params func(TunnelServer_conn InterfaceID: 0xea58385c65416035, MethodID: 3, InterfaceName: "tunnelrpc/tunnelrpc.capnp:TunnelServer", - MethodName: "connect", + MethodName: "obsoleteDeclarativeTunnelConnect", }, Options: capnp.NewCallOptions(opts), } if params != nil { call.ParamsSize = capnp.ObjectSize{DataSize: 0, PointerCount: 1} - call.ParamsFunc = func(s capnp.Struct) error { return params(TunnelServer_connect_Params{Struct: s}) } + call.ParamsFunc = func(s capnp.Struct) error { + return params(TunnelServer_obsoleteDeclarativeTunnelConnect_Params{Struct: s}) + } } - return TunnelServer_connect_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} + return TunnelServer_obsoleteDeclarativeTunnelConnect_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} } func (c TunnelServer) Authenticate(ctx context.Context, params func(TunnelServer_authenticate_Params) error, opts ...capnp.CallOption) TunnelServer_authenticate_Results_Promise { if c.Client == nil { @@ -2942,7 +2944,7 @@ type TunnelServer_Server interface { UnregisterTunnel(TunnelServer_unregisterTunnel) error - Connect(TunnelServer_connect) error + ObsoleteDeclarativeTunnelConnect(TunnelServer_obsoleteDeclarativeTunnelConnect) error Authenticate(TunnelServer_authenticate) error @@ -3006,11 +3008,11 @@ func TunnelServer_Methods(methods []server.Method, s TunnelServer_Server) []serv InterfaceID: 0xea58385c65416035, MethodID: 3, InterfaceName: "tunnelrpc/tunnelrpc.capnp:TunnelServer", - MethodName: "connect", + MethodName: "obsoleteDeclarativeTunnelConnect", }, Impl: func(c context.Context, opts capnp.CallOptions, p, r capnp.Struct) error { - call := TunnelServer_connect{c, opts, TunnelServer_connect_Params{Struct: p}, TunnelServer_connect_Results{Struct: r}} - return s.Connect(call) + call := TunnelServer_obsoleteDeclarativeTunnelConnect{c, opts, TunnelServer_obsoleteDeclarativeTunnelConnect_Params{Struct: p}, TunnelServer_obsoleteDeclarativeTunnelConnect_Results{Struct: r}} + return s.ObsoleteDeclarativeTunnelConnect(call) }, ResultsSize: capnp.ObjectSize{DataSize: 0, PointerCount: 1}, }) @@ -3070,12 +3072,12 @@ type TunnelServer_unregisterTunnel struct { Results TunnelServer_unregisterTunnel_Results } -// TunnelServer_connect holds the arguments for a server call to TunnelServer.connect. -type TunnelServer_connect struct { +// TunnelServer_obsoleteDeclarativeTunnelConnect holds the arguments for a server call to TunnelServer.obsoleteDeclarativeTunnelConnect. +type TunnelServer_obsoleteDeclarativeTunnelConnect struct { Ctx context.Context Options capnp.CallOptions - Params TunnelServer_connect_Params - Results TunnelServer_connect_Results + Params TunnelServer_obsoleteDeclarativeTunnelConnect_Params + Results TunnelServer_obsoleteDeclarativeTunnelConnect_Results } // TunnelServer_authenticate holds the arguments for a server call to TunnelServer.authenticate. @@ -3552,48 +3554,48 @@ func (p TunnelServer_unregisterTunnel_Results_Promise) Struct() (TunnelServer_un return TunnelServer_unregisterTunnel_Results{s}, err } -type TunnelServer_connect_Params struct{ capnp.Struct } +type TunnelServer_obsoleteDeclarativeTunnelConnect_Params struct{ capnp.Struct } -// TunnelServer_connect_Params_TypeID is the unique identifier for the type TunnelServer_connect_Params. -const TunnelServer_connect_Params_TypeID = 0xa766b24d4fe5da35 +// TunnelServer_obsoleteDeclarativeTunnelConnect_Params_TypeID is the unique identifier for the type TunnelServer_obsoleteDeclarativeTunnelConnect_Params. +const TunnelServer_obsoleteDeclarativeTunnelConnect_Params_TypeID = 0xa766b24d4fe5da35 -func NewTunnelServer_connect_Params(s *capnp.Segment) (TunnelServer_connect_Params, error) { +func NewTunnelServer_obsoleteDeclarativeTunnelConnect_Params(s *capnp.Segment) (TunnelServer_obsoleteDeclarativeTunnelConnect_Params, error) { st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) - return TunnelServer_connect_Params{st}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Params{st}, err } -func NewRootTunnelServer_connect_Params(s *capnp.Segment) (TunnelServer_connect_Params, error) { +func NewRootTunnelServer_obsoleteDeclarativeTunnelConnect_Params(s *capnp.Segment) (TunnelServer_obsoleteDeclarativeTunnelConnect_Params, error) { st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) - return TunnelServer_connect_Params{st}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Params{st}, err } -func ReadRootTunnelServer_connect_Params(msg *capnp.Message) (TunnelServer_connect_Params, error) { +func ReadRootTunnelServer_obsoleteDeclarativeTunnelConnect_Params(msg *capnp.Message) (TunnelServer_obsoleteDeclarativeTunnelConnect_Params, error) { root, err := msg.RootPtr() - return TunnelServer_connect_Params{root.Struct()}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Params{root.Struct()}, err } -func (s TunnelServer_connect_Params) String() string { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Params) String() string { str, _ := text.Marshal(0xa766b24d4fe5da35, s.Struct) return str } -func (s TunnelServer_connect_Params) Parameters() (CapnpConnectParameters, error) { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Params) Parameters() (CapnpConnectParameters, error) { p, err := s.Struct.Ptr(0) return CapnpConnectParameters{Struct: p.Struct()}, err } -func (s TunnelServer_connect_Params) HasParameters() bool { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Params) HasParameters() bool { p, err := s.Struct.Ptr(0) return p.IsValid() || err != nil } -func (s TunnelServer_connect_Params) SetParameters(v CapnpConnectParameters) error { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Params) SetParameters(v CapnpConnectParameters) error { return s.Struct.SetPtr(0, v.Struct.ToPtr()) } // NewParameters sets the parameters field to a newly // allocated CapnpConnectParameters struct, preferring placement in s's segment. -func (s TunnelServer_connect_Params) NewParameters() (CapnpConnectParameters, error) { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Params) NewParameters() (CapnpConnectParameters, error) { ss, err := NewCapnpConnectParameters(s.Struct.Segment()) if err != nil { return CapnpConnectParameters{}, err @@ -3602,82 +3604,82 @@ func (s TunnelServer_connect_Params) NewParameters() (CapnpConnectParameters, er return ss, err } -// TunnelServer_connect_Params_List is a list of TunnelServer_connect_Params. -type TunnelServer_connect_Params_List struct{ capnp.List } +// TunnelServer_obsoleteDeclarativeTunnelConnect_Params_List is a list of TunnelServer_obsoleteDeclarativeTunnelConnect_Params. +type TunnelServer_obsoleteDeclarativeTunnelConnect_Params_List struct{ capnp.List } -// NewTunnelServer_connect_Params creates a new list of TunnelServer_connect_Params. -func NewTunnelServer_connect_Params_List(s *capnp.Segment, sz int32) (TunnelServer_connect_Params_List, error) { +// NewTunnelServer_obsoleteDeclarativeTunnelConnect_Params creates a new list of TunnelServer_obsoleteDeclarativeTunnelConnect_Params. +func NewTunnelServer_obsoleteDeclarativeTunnelConnect_Params_List(s *capnp.Segment, sz int32) (TunnelServer_obsoleteDeclarativeTunnelConnect_Params_List, error) { l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz) - return TunnelServer_connect_Params_List{l}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Params_List{l}, err } -func (s TunnelServer_connect_Params_List) At(i int) TunnelServer_connect_Params { - return TunnelServer_connect_Params{s.List.Struct(i)} +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Params_List) At(i int) TunnelServer_obsoleteDeclarativeTunnelConnect_Params { + return TunnelServer_obsoleteDeclarativeTunnelConnect_Params{s.List.Struct(i)} } -func (s TunnelServer_connect_Params_List) Set(i int, v TunnelServer_connect_Params) error { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Params_List) Set(i int, v TunnelServer_obsoleteDeclarativeTunnelConnect_Params) error { return s.List.SetStruct(i, v.Struct) } -func (s TunnelServer_connect_Params_List) String() string { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Params_List) String() string { str, _ := text.MarshalList(0xa766b24d4fe5da35, s.List) return str } -// TunnelServer_connect_Params_Promise is a wrapper for a TunnelServer_connect_Params promised by a client call. -type TunnelServer_connect_Params_Promise struct{ *capnp.Pipeline } +// TunnelServer_obsoleteDeclarativeTunnelConnect_Params_Promise is a wrapper for a TunnelServer_obsoleteDeclarativeTunnelConnect_Params promised by a client call. +type TunnelServer_obsoleteDeclarativeTunnelConnect_Params_Promise struct{ *capnp.Pipeline } -func (p TunnelServer_connect_Params_Promise) Struct() (TunnelServer_connect_Params, error) { +func (p TunnelServer_obsoleteDeclarativeTunnelConnect_Params_Promise) Struct() (TunnelServer_obsoleteDeclarativeTunnelConnect_Params, error) { s, err := p.Pipeline.Struct() - return TunnelServer_connect_Params{s}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Params{s}, err } -func (p TunnelServer_connect_Params_Promise) Parameters() CapnpConnectParameters_Promise { +func (p TunnelServer_obsoleteDeclarativeTunnelConnect_Params_Promise) Parameters() CapnpConnectParameters_Promise { return CapnpConnectParameters_Promise{Pipeline: p.Pipeline.GetPipeline(0)} } -type TunnelServer_connect_Results struct{ capnp.Struct } +type TunnelServer_obsoleteDeclarativeTunnelConnect_Results struct{ capnp.Struct } -// TunnelServer_connect_Results_TypeID is the unique identifier for the type TunnelServer_connect_Results. -const TunnelServer_connect_Results_TypeID = 0xfeac5c8f4899ef7c +// TunnelServer_obsoleteDeclarativeTunnelConnect_Results_TypeID is the unique identifier for the type TunnelServer_obsoleteDeclarativeTunnelConnect_Results. +const TunnelServer_obsoleteDeclarativeTunnelConnect_Results_TypeID = 0xfeac5c8f4899ef7c -func NewTunnelServer_connect_Results(s *capnp.Segment) (TunnelServer_connect_Results, error) { +func NewTunnelServer_obsoleteDeclarativeTunnelConnect_Results(s *capnp.Segment) (TunnelServer_obsoleteDeclarativeTunnelConnect_Results, error) { st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) - return TunnelServer_connect_Results{st}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Results{st}, err } -func NewRootTunnelServer_connect_Results(s *capnp.Segment) (TunnelServer_connect_Results, error) { +func NewRootTunnelServer_obsoleteDeclarativeTunnelConnect_Results(s *capnp.Segment) (TunnelServer_obsoleteDeclarativeTunnelConnect_Results, error) { st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) - return TunnelServer_connect_Results{st}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Results{st}, err } -func ReadRootTunnelServer_connect_Results(msg *capnp.Message) (TunnelServer_connect_Results, error) { +func ReadRootTunnelServer_obsoleteDeclarativeTunnelConnect_Results(msg *capnp.Message) (TunnelServer_obsoleteDeclarativeTunnelConnect_Results, error) { root, err := msg.RootPtr() - return TunnelServer_connect_Results{root.Struct()}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Results{root.Struct()}, err } -func (s TunnelServer_connect_Results) String() string { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Results) String() string { str, _ := text.Marshal(0xfeac5c8f4899ef7c, s.Struct) return str } -func (s TunnelServer_connect_Results) Result() (ConnectResult, error) { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Results) Result() (ConnectResult, error) { p, err := s.Struct.Ptr(0) return ConnectResult{Struct: p.Struct()}, err } -func (s TunnelServer_connect_Results) HasResult() bool { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Results) HasResult() bool { p, err := s.Struct.Ptr(0) return p.IsValid() || err != nil } -func (s TunnelServer_connect_Results) SetResult(v ConnectResult) error { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Results) SetResult(v ConnectResult) error { return s.Struct.SetPtr(0, v.Struct.ToPtr()) } // NewResult sets the result field to a newly // allocated ConnectResult struct, preferring placement in s's segment. -func (s TunnelServer_connect_Results) NewResult() (ConnectResult, error) { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Results) NewResult() (ConnectResult, error) { ss, err := NewConnectResult(s.Struct.Segment()) if err != nil { return ConnectResult{}, err @@ -3686,37 +3688,37 @@ func (s TunnelServer_connect_Results) NewResult() (ConnectResult, error) { return ss, err } -// TunnelServer_connect_Results_List is a list of TunnelServer_connect_Results. -type TunnelServer_connect_Results_List struct{ capnp.List } +// TunnelServer_obsoleteDeclarativeTunnelConnect_Results_List is a list of TunnelServer_obsoleteDeclarativeTunnelConnect_Results. +type TunnelServer_obsoleteDeclarativeTunnelConnect_Results_List struct{ capnp.List } -// NewTunnelServer_connect_Results creates a new list of TunnelServer_connect_Results. -func NewTunnelServer_connect_Results_List(s *capnp.Segment, sz int32) (TunnelServer_connect_Results_List, error) { +// NewTunnelServer_obsoleteDeclarativeTunnelConnect_Results creates a new list of TunnelServer_obsoleteDeclarativeTunnelConnect_Results. +func NewTunnelServer_obsoleteDeclarativeTunnelConnect_Results_List(s *capnp.Segment, sz int32) (TunnelServer_obsoleteDeclarativeTunnelConnect_Results_List, error) { l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz) - return TunnelServer_connect_Results_List{l}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Results_List{l}, err } -func (s TunnelServer_connect_Results_List) At(i int) TunnelServer_connect_Results { - return TunnelServer_connect_Results{s.List.Struct(i)} +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Results_List) At(i int) TunnelServer_obsoleteDeclarativeTunnelConnect_Results { + return TunnelServer_obsoleteDeclarativeTunnelConnect_Results{s.List.Struct(i)} } -func (s TunnelServer_connect_Results_List) Set(i int, v TunnelServer_connect_Results) error { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Results_List) Set(i int, v TunnelServer_obsoleteDeclarativeTunnelConnect_Results) error { return s.List.SetStruct(i, v.Struct) } -func (s TunnelServer_connect_Results_List) String() string { +func (s TunnelServer_obsoleteDeclarativeTunnelConnect_Results_List) String() string { str, _ := text.MarshalList(0xfeac5c8f4899ef7c, s.List) return str } -// TunnelServer_connect_Results_Promise is a wrapper for a TunnelServer_connect_Results promised by a client call. -type TunnelServer_connect_Results_Promise struct{ *capnp.Pipeline } +// TunnelServer_obsoleteDeclarativeTunnelConnect_Results_Promise is a wrapper for a TunnelServer_obsoleteDeclarativeTunnelConnect_Results promised by a client call. +type TunnelServer_obsoleteDeclarativeTunnelConnect_Results_Promise struct{ *capnp.Pipeline } -func (p TunnelServer_connect_Results_Promise) Struct() (TunnelServer_connect_Results, error) { +func (p TunnelServer_obsoleteDeclarativeTunnelConnect_Results_Promise) Struct() (TunnelServer_obsoleteDeclarativeTunnelConnect_Results, error) { s, err := p.Pipeline.Struct() - return TunnelServer_connect_Results{s}, err + return TunnelServer_obsoleteDeclarativeTunnelConnect_Results{s}, err } -func (p TunnelServer_connect_Results_Promise) Result() ConnectResult_Promise { +func (p TunnelServer_obsoleteDeclarativeTunnelConnect_Results_Promise) Result() ConnectResult_Promise { return ConnectResult_Promise{Pipeline: p.Pipeline.GetPipeline(0)} } @@ -4383,257 +4385,259 @@ func (p ClientService_useConfiguration_Results_Promise) Result() UseConfiguratio return UseConfigurationResult_Promise{Pipeline: p.Pipeline.GetPipeline(0)} } -const schema_db8274f9144abc7e = "x\xda\xccz}\x90\x14uz\xff\xf3t\xcf\xd2\xbb\xb0" + - "\xcbL\xdbc\xfdv)\xf67\xc7\x8bQ8!\"G" + - "\xa2\x9b\xe4\xf6\x0d\xb8]\x0ea{g\x17u\xe5R\xf6" + - "\xce|w\xb6a\xa6{\xe8\xee\x01\x96\xe0\xf1R\x10e" + - "\x03'x\x90\x02\x0f\xaf\x00\x8f\xf8\x12\xbd\x13\x0f+\xa7" + - "\x11K\x93\xab\xa89\x13\xe5\x82)\xbd\x98\x8a\x11\xa8T" + - "\xac\xb3<\xd1\x94eJ\xed\xd4\xf3\xed\xd7\x1d\x96\x05L" + - "R\x95\x7f`\xea\xe9\xe7\xfb\xf2\xbc}\x9e\x97\xef\xde\xf4" + - "\xe3\xc9m\xc2\x82\x9a\xeb\x93\x00\xeaS5\x93\\6\xf7" + - "\x97\x9b\x8e\\\xf7\xd7\xdbA\x9d\x86\xe8~\xf7\xf9e\xe9" + - "\xcf\x9c\xed\xff\x045\xa2\x04\xb0pD\xda\x84\xca\x1eI" + - "\x02PvI\xff\x06\xe8\xd6\xfc\xd6\x9b\xef\x96\xdf\x95v" + - "\x80<-\xce,\x10s\xa9v\x19*\xdbj\x89\xf9\x9e" + - "\xda\x0d\x80\xee\xef\x97^?\xf6;\x07~A\xccB\xc4" + - "\x0c\xb8\xf0|\xed&T>\xe3\x9c\xffQ\xbb\x12\xd0\xfd" + - "x\x7f\xe3\x9f\x1f\xfd\xfbWv\x82|=\x82\x7fvC" + - "\xdd\xaf\x10P\x99Q\xf7\x13@\xf7\x1fn\xda|\xee\xee" + - "\x8f\xf7\xdd7\xf6\xdc\x04\xf1\xbdT7\x8a\xca\xdbu\x12" + - "\x88\xeeCw\xa5\xff\x16\x8f|\xba\x0f\xe4\x1bh\x1b\xa4" + - "\xcf\xcf\xd4M\x16\x00\x95\xbf\xabk\x05t_\xbf\xf1\xf9" + - "\xe7\xf6\xfe\xf4\xde\x1f\x80z=\"x\xeb?\xa8\xfbO" + - ":\x07'\x13\xc3G?\xfaz\xe2\xc9\xd7\xaf\xf9!g" + - "p\x8f\x9f\xbe\xfd\xe9\xbd?\xfd\xda\xfb\xd0/H\x98\x00" + - "X8g\xb2E\xbc\x8b&\x93.\xf6\xbfujEi" + - "\xdf\x83\xc7\xbcK\xf3\xbd\xae\x9d\"\x08\x90pwt\x7f" + - "Z\xea\x7f8\xfb\xb0/N\x0d}\xaa\x9br\x01\x01\x17" + - "6O\xc9 \xa0\xbb\xe8W\xe7W\xde\xf6\xf4\xd0#>" + - "\x07\xbf\xe9\xad\xf5O\xd3\xe6\xdd\xf5t\x91W6\xa4v" + - "\xb7\xff\xee\xfd\x8fT\x9b\x85\xefU\xaa\x1fEeg=" + - "\xfd\xdcV\x7f;\xed7z\xeb\xa9U\x1f\xff\xb1\xfd8" + - "\xa8\xf30\xe1\xfe|\xd7\xd9\xf5s\x1e\x1bz\x99\xdf[" + - "\x04X\xf8Y\xc3/i\xeb\x86\xa9\xa4\xcb\x86\xbf\x9c\xbb" + - "\xe2\xfes\xcbO\xd0\xd61\xbbx\x97xrj\x0b*" + - "\xa7\xa6\x92i\x9e\xe5\xdco\xdc\xb8\xea\x85\x17\x9e*\x9c" + - "\xa8\xbe\x087\xf9\x9d\xc9e\xa8\x94\x92\xc4\xad'\x89\xfb" + - "\xdan|\xe7\xc5\x05\x89\xbf\x88\x1b\xb29\xf5>\x1d\xbe" + - " E\x0cw}\xfe\xcc_-\xf9\xf0\xcc\xb3q\x13\x9d" + - "N\x09d\xa2\xf3)\x12|`\x14K\xef\xb4\xb4\xbd\x00" + - "\xea\x0d\x88\xee\x9a\x03\x9b\x9d\xae\x83{\\\xe8G\x09\x05" + - "\xf2\x0ay\x13m\xd6$\x93\x835\x7f\xd0\xd1`|\xb8" + - "\xfd\xc5*o\xe4\xa7V\xe4e\xa8\xec\x92\xe9j;\xe5" + - "\x9f\x00~\xfa\xf8\xbd{\xbb\xcf.~Y\x9d\x86\x89j" + - "\xa1g\\\xb3\x09\x95E\xd7\xd0\xcf\x05\xd7p\xfb\x84\x1a" + - "\xacb\xe7Rk\xca\x1aT*\x0a\xfd\\\xa7p\xf6e" + - "w}\xff\x81\x9a\xf3\xdf\x7f\xb9Z\xa5\xe4\xe2\x0b\xefI" + - "[\xa8\xecK\xd3\xcf=\xe9\xff'\x02\xba\xd3\x9e\xfa\xbd" + - "\x1fw\xe4\xdf\xfe\xc58Q\xa444^P\x9a\x1a\xe9" + - "\xd7\xb5\x8d$\xe3\xd9y'\xfe\xe8\xdf\xf7\x9c>\x13\xf7" + - "\x94u\x8d\xdcew6\x92\xc2\xee\xfd\xfa\xc8\xa6\x15\xd7" + - "\x8d\xbeYm \xcey\xbcq\x14\x95S|\xbbg\xf9" + - "v\xc2y\xadi\xeb?~\xf3\x9d\x98\xd3\xceiz\x0f" + - "!\xe1\xaeXu\xd7\x9a\xba{\xce\x9e\x8d\x1f\xd4\xdc\xe4" + - "\x99\xae\x89\x0e:)?\xa0<\x7f\xf4\xcf\xce\xd1AR" + - "\xb5\xba\xd5\xa6\x01TX\x13WO\xd3#\x02\xc4\x82g" + - "<\xc7\xf9\xce\xf4\x16TJ\xd3\xb9\xe3L\xa7{-\xba" + - "\xbb\x9d\xad\xbe\xe5\x8e\xf7A\x9e&\x8e\xc1\x8a\xc7\x88\xf3" + - "\xd9\xe9<\x94\xa7\xdf\x8b\xca\xa9f\x09\xc0\xfd^a\xe0" + - "\xd5\x8f:\x8f\xfe\xa6zs.\xd0\xf1\xe6\x16T\x9e!" + - "\xbe\x85'\x9a\xb9}\x16.\xf8\x93\x0f\x0e<\xdc\xf9\xd1" + - "E\xbb\x7f\xf1\xff;Pi\xc8\xd0=\xea2\xdfRn" + - "\xcd\xf0\xcd\xbf\xbbx\xe5\xad3_\xba\x10\xd7\xc4\x8c\xcc" + - "\x05\x1e\xf9\x19\xd2\xc4\xd0-\xbf\xfe\xd6u\xdf\xfb\x9b\x0b" + - "U\xf6\xe3\x8c\xfd\x99\xb9\xa80\xbe\xa3F\xcc\x1f.\xfd" + - "\xe1\x99i\xc9i\x9fT]t\x12\xf1\xee\xcc\xacA\xe5" + - "\x10\xf1.<\x90y\x99.z\xc7{\x0fnh\xfd\xc1" + - "'\x9f\x92\\b\x15\xd0\xed\x9a1\x80\xcaC3h\xe7" + - "C3(\x96\x96?\xf1\xf67\x87\x0f\xbc\xf2\xd9\xb8\xd0" + - "\xbdd\xe6vT\xee\x9cI\xdc\xfd3\x09\xae\xfeT:" + - "|v\xeb\xbf\xfc\xe1\xe7q\xa9\xfe`\xd6{$\x95:" + - "\x8b\xa4\xda\xfc\xe1\xa1\xae\xfbW?\xf1\xe5\x18O\x9b\xf5" + - "\x1c1l\xe3\x0ca0\x8e\xe7iGgu\xa0rb" + - "\x16\x9d\xf7\xe4\xacV\x98\xe7:\x15\xc3`E\xab\x9c\xc8" + - "\xfdv\xf037?\xa7\x95\x8drK{\xc5\x19f\x86" + - "\xa3\xe74\x87\xf5\xb2V\xbbl\x1a6\xebATSb" + - "\x02 \x81\x00\xb2\xb6\x06@\xbd[D\xb5(\xa0\x8c\x98" + - "&\xb8\x96u\"\x0e\x8b\xa8:\x02\xca\x82\x90&D\x90" + - "\xd7\xcd\x04P\x8b\"\xaa\x1b\x05D1Mx'W\x1e" + - "\x00P7\x8a\xa8\xee\x10\xd0-3\xab\xa4\x19\xcc\x80\xa4" + - "\xb3\xc4\xb2\xb0\x1e\x04\xac\x07t-\xe6X#\xda`\x11" + - "\x92,F\x96\xd6lp\xb0\x01\x04l\x00t\x87\xcd\x8a" + - "e\xf7\x1b\x0e\xea\xc5^6d1\x1b\x87q\x12\x088" + - "\x09p\"\xf1:M\xc3`9'[\xc9\xe5\x98m\x03" + - "\x90d\xb5\xa1ds\x1e\x04Po\x14Q\xbd%&\xd9" + - "\"\x92\xec\x1b\"\xaam\x02\xba6\xb3\xd63k\xb9\x89" + - "9\xcd\xd1Mc\x85&\x96Xx\xed\\Qg\x86\xd3" + - "iB\xd2\x18\xd2\x0b\x98\x8aB\x01\x10S\x13_l\xc9" + - "F\xddvt\xa3\xd0\xc7\xe9\xad=fQ\xcf\x8d\xd0\xed" + - "\xea\xb9&\x9b[h\x0f\xf9\xda\x01\x00\x14d\xb9\x03\xa0" + - "U/\x18\xa6\xc5\xdc\xbcn\xe7H(\x10s\xce\x96A" + - "\xad\xa8\x199\x16\x1e4\xe9\xe2\x83\xbc\x03\xb2\\\x8e\xf9" + - "Z\xcc\xda\xb3{4K\x13K\xb6Z\x1f\xeac\xc9\x00" + - "\x80\xbaXD\xb5'\xa6\x8f\xdb\x96\x01\xa8\xcbET\xef" + - "\x88Y\xba\xbf\x03@\xed\x11Q]-\xa0kZzA" + - "7:\x19\x88V\xdc`\xb6ch%\x06\x00\x81\xc2\xb6" + - "\x98eR\xa2\x8d\xa9\x08\xa5\xab4Us\xb1\x00]\xac" + - "X4o7\xadb~\xa5w\x8eI\xda\xe6\xa6\x0c\x97" + - "I\xe3X\x9e\x1b\x87\xe4\xd6sl~\xc5f\xde\xba\x8a" + - "\xc5\x0d9\xbb\x97\xd9\x95\xa2c\x03\xa8\x89P\xfc\x86\x16" + - "\x00\xb5VD5-`\xab\xc5\x190\x15\x81z\xd5U" + - "/\xa7\xeb\x8aa\xb1\x82n;\xcc\xf2\xc8\xb3[I\xe1" + - "%;~ \xf9_JDu\xba\x80n\xc1\xd2r\xac" + - "\x87Y\xa8\x9b\xf9\x15\x9aafE\x96\xc3\x1a\x10\xb0f" + - "bOZ\xaa\xe9E\x96\xf7\xa4\x9b\x9f\xcb\xf0\xff)z" + - "\xeb]\xd7\x0b\xdf\x81(|\x1b\xf0K\xd7\x8f\xdfMQ" + - "\xfc6\x08_\xb8\x17\x07p\x83\xf8\xb9\xeb\x870E\x84" + - "#\xa2\xba\x95\"\xa2R&\x9d\xda \x9a\x16\xa6\"\x94" + - "\xf4\xb5\xc3\xf2\x05\xd2\xb4\x01\xad,G\x8a\xc6T\x90\xed" + - "=\x06)o\x0ec**e\xfce\x16[\xcf,\x9b" + - "\xf5@\xd227\x8e`*\xca\xfaUZ\x9fz\xb5Z" + - "\x0f\x0c\x1d\xae\x9ax\xbd\xc5r\x1ed\xf8\xcb{2\x9e" + - "\xd1\xd2\xa1\xd1\xee\x99\x19\x01Z\x18$\xdb\x06\x01\xd4\xad" + - "\"\xaa\xbbcA\xb2\x8b4\x7f\x9f\x88\xea~\x01e\xd1" + - "\xc7\xc3}\x14N{ET\x0f\x0b('\x12i*f" + - "\xe5C\x14N\xfbET\x8f\x08ca\x8f\xadg\x86\xb3" + - "X/\x80\xc4\xec\x88JW\\\xac\x17\x18\x88\xf6\xffB" + - "\xc0\x8d\xd1\x87\xaf\x8dP\x0f1\xe7%\xe9\xeaET\x1b" + - "\x09\xd8\xe9+s\x08\x02\xe8\xb4\xb0d\xbe\xfci\x9d\xf4" + - "\xaf\x0f\xd3=\xfe.\x96\x8f\xd4\x8d\xe1a\x87\xe8\xb0\x83" + - "\"\xaa?\x8a)\xfd\xa8\x05\xa0\x1e\x11Q}B@\xf4" + - "u\xfe\xd81\x00\xf5\x09\x11\xd5\x9f\x91\xce\x05O\xe7\xcf" + - "\xcc\xa5\x0eKD\xf55\xd2\xb9\xe8\xe9\xfcU\x0a\xbe\xd7" + - "DT\xdf\x12P\xaeI\xa4\xb1\x06@~\x93\xecxF" + - "D\xf5\xddK\xe1Z\xaehV\xf2CE\x0d2\x16\xcb" + - "w/\x0e\xe9F\xa5\xd4c\xb1\xf5:\x9a\x15\xbb\xddq" + - "XI*;v\x90\xa2\x92\x8eV\xb0q*`\x8f\x88" + - "\x98\x8a\x8aN@\"\x86{\xa2\xc5\xf2\xab\x98e\xeb\xa2" + - "i\x84YF7\x1cf8\xcb5\x90\x06Y1\xa4N" + - "\x80B\xbd~,Q$\xf9\xb0`F\xc8\x89\x05\x02\xfc" + - "\xe9\xae\xeb+q\x09\xe9\xa6MDu\xb9\x80\xcd\xf8%" + - "\x91I\x8f\xdd\xbd\x00j\x97\x88j\x9f\x80\xcd\xc2\x17D" + - "&M\xaa\x03\x11\xee'\x87\x1d\xa7\x8c\xa9\xa8\x18\xf5\x8d" + - "\xbd\x81\x0d\xdafn-\x03$\xf8\x0c+#\xff\xeb\xb0" + - "\x0f\xe7 \x16\xf3\x98\x8a\xba\xc9*O\x11/\x95\xcb[" + - "\xa9r0-\x9e*\xa3\xc4us$D\xe0\x1d\xdd\x03" + - "\x91\x04\xb2\xd0\xe6\x89\xa5\x0eF\xf7\xcf\xe4\xb4\x8a\xcd\xc6" + - "\x16!\xedC\x0e\x88\xcc\x0aq\xd7\x1e6+\xc5|/" + - "\x03\xc9\xb1F\x10A@\x9c\x18\x8d\x17\x9b]1\xc5{" + - "n<~\x82\x0d\xf3\xeb@<\xbf\xfa\xea\xef'\xf5\xf7" + - "\x89\xa8\x96\x05t\x8b\x84gF\x97\xc9\xc3=\xb8\xaeG" + - "\xec1\xb9sJ \xa0\x04\xe8V\xca\xb6c1\xad\x04" + - "\x18z\x1b\xf1O\xbd\x8a\xb4U\x05\x9f=Z\x92\xc7\xfd" + - "\xff\xa5\"\xe1\xea\xb3\xbd\x97y\xc7\xe4\xfac\xb1\xd4\x9b" + - "\xf3W#_\xdei\x1a\xd2U\xd7s>\x82y\xd9f" + - "\xbe_=P\xa9\x19\xa4\xe19\x946f\x8b\xa8\xde\x14" + - "O\xc3\xf3HE7\x88\xa8~C@\x89Y\x94Q\xc3" + - "\x99\x80w\xe8\x16\xdb\xab]1\x15M|.\x7f\x9dX" + - "Y\xaf\x9b\xc6En83\x0a\x97\xd0\x84\xdd7\xc7\xec" + - "\x1a\x98\xf0\xb6\xc1\xc8\xae\xd2Z6\x12X)\xc3J\x9a" + - "\x1e\xa1\x91o\xdcv\x90\xbe\x1d\xf1LX\xfe\xfae\x82" + - "W$\xb4z\xd6\xa2K\xc6\xf2\xech,\xa5\x06\x97\xdc" + - "E\xdd\xc4n\x11\xd5\x83\xb1K\x1e\xe8\x88\xa5\xd4 \xcf" + - "\x1e\"\x03\x1f\x16Q}T@\xf4\xd3\xecq\x82\xfcG" + - "ETO\x0a\x1c\xb0\xbb\xda;M\x03\xfdK\xd8\x00a" + - "G1\xcc4\xcb\x19d\x1a:\xdd\x86\xc3\xac\xf5\x1a\x16" + - "\x03H\xd8\xe2\xe8%fV\x9c\x10\"J\xdaF^\x82" + - "a\xbe\xcb[%i\x8e\x8du `\x1dE\xa4\xcd\xac" + - "N\x8b\xe5\x91\xac\xa1\x15{4\xd1\x19\xbe\x12\x05\x8d\x05" + - "\xf1\xe48\xea\xa1\x02n\xb3\x88\xea}\x04%\x18\x9b;" + - "\xc9;\xd7\x80\xc0\x91\x84d^\xd7\x11\x95t\x8cK\xcf\xf9\x96\x9b\x90\xc9i\xc5\xeerT#\xd9" + - "\xed\x15\xc7\xac\x94!\x93\xd7\x1c\x96\x0f\x13\xb5U1\x96" + - "Zf\xa9\x0f\x99U\xd2\x0d\xad\x08\xe1\x97\x89|1Y" + - "\xa9\xe8\xf9p\xef\x09\x0b;w\x88iN\xc5b6\x89" + - "v\x89\x8c+T{t\xa6\xdc\xd2\xa7\x15\xaaF\x11s" + - "\xa3\xf4\x10\xa2\xdd\xbc\x9b\xa3\xec\x90\x8cGaf\xbdV" + - "\xac\xb0+)\x06'lnz[\xbd\xe6\xe8r=p" + - "08\xbb|5\xdf_\x95y\xbd|x\xd1\xdc\xa5#" + - "\x126\x94\xd5\xf2g1]B\x94\xf3\x02k\x0d\xf9=" + - ".dh\xef\x98\xdf\x84\x93M\xdfo\xaeT\x13\x05\xe6" + - "x\xbf\xba\x8d!\x93\xca\x03I+\xd9_qu/\xb3" + - "\x93W\xa2\xc5hVy\xf9\xfc\xdd\xd5\xd7\xd7\x13\x8d;" + - "D\x0f\xfco\x0a\xf1\xae\x1d{\x01\xb2m\x14\xb8\xcb1" + - "\xd4\xa1\xd2\xcdq\xa7\x8b\xc8}\x18U\xbd\x8a\xca\xf1\xa5" + - "\x87\xe8\xab1\xea\x8b\x94;9.\xac&\xfa0\xc7\xbb" + - "v\x0f\xef\x18\xdf>O\xf42\xc7;\xf4\xf0\xae\xc4\xf7" + - "/\x12}c\x1c\xef*8:\x06~%\xd1\xc3\xbbm" + - "\x1c\xa7v\x10}/\xc7\xbb\x84\x87w{\xf0i\x80\xec" + - "^\xa2\x1f\xe6xW\xe3\xe1\xdd!|\x0e {\x98\xe8" + - "\x8fr\xbc\x9b\xe4\xe1\xddq\xce\xffh\x88\xb3S:<" + - "\xbc;\xc1\xf11\xc4Y\xb7b\x15\xb3\x8e\xa5\x1b\x80\x85" + - "(6r\xe5o3Vn\x87dQ_\xcf\xc2\\\x94" + - "\xd7\xb5\xe2\xe2\x8aV\x84L\xd6\xd1rk\xa3\xd2\xbeh" + - "wiF\xde\xc6am-\xa3\x0c&\xc5s\xbdS\xb4" + - "W1K\x1f\x02\x8c\x9a\x81\xb0\xf6I\xf6\x98fuI" + - "\xc4kJfy\xe0\x17~+i\x1b\xbb\xf3E\xd6\x89" + - "A\x05$\x1aQ\x06\xd5\xe9\x8bi\x18\xe8\x95%}z" + - "fl\xbdQ\xf6\xdb\x8b\xa0n\xe9k\xad*H\xd8\xc6" + - "2\xcb9\x9d&\x1a\x8enT\xd8E\x1b\xe4\x86+\xc6" + - "Z\x96_\x82F\xce\xcc\xebF\x01.\xeak\xc4KM" + - "\x99b\x85\x1a\x8ff\x8c=\xd3\xc9sZ@\xe0\xd0E" + - "e\x87\xdc\x12M\x07Zs|U\xab\xc54;\xd6\xd8" + - "Np\x9a?\x15\xf5\x82\xcc\x9b\x04\xd4\x00\x84OZ\x18" + - "<\x0b\xc8'6\x81 ?&a\xf4\x9a\x82\xc1\xe3\x89" + - "\xfc\x90\x05\x82|@B!|k\xc4\xe0\x9dP\xde5" + - "\x0a\x82\xbcSB1|\xfe\xc3`\xe6.\x8ft\x80 " + - "\x97$L\x84o\xa1\x18\x0c\xece\x8dJ\xab;%\xac" + - "\x09\x1f\x161x\x15\x92o\xdb\x0e\x82\xbcDr\x83\x0e" + - "\x0aZ=1\xda\xd0\x0d\x00\x032\x1c2\xda\xd0\x0d\xe6" + - "T\x18tZ\x00m\xb8\xc5\x87\xe76t\x83I-$" + - "s\x9a\xc3\xda\xa8=\xf5>\xa2\x0f\xde\xd0\x86\xf1\x09\xa8" + - "x\xa9\x9eh\xfc\xda\xba#\xaa\xff\xc2\x11\xd6hT\xfe" + - "\x85}\xe8\x9e\xc7\xe3\xa5\xb5?N9\xb4\xdd\x1f\xc6\x9c" + - "\x8c\x8dSNP\xbd}RD\xf5\x0d!*\x1a\x02\x9f" + - "\x0e\x86\x86hZAc<\xc1\xec\xd0\xf7|\xbf\xec\xad" + - "\x9e \xbays\x98\x97\xc5\xe8meC\x94\x0e\xe2c" + - "\xc5\xa9\xb1\xb1\"\x06-\xb94&{\xc4\x87\x8cS/" + - "\xd3\xdf\xc5\x1bL\x9e\xce\x12\xdc%\x83GT\x0c\x1e\xbc" + - "e\x99\\\xabAr\x83&\x14\x83\\\x08U&\xbb\xca" + - "N\xbc\x97e\xfe;\xc9z\x1c\x07\xf1\xceI\x92Gz" + - "\x02\x85\xfb\xae\x89\x8d\xf6\x8a\xa6\xdfD&W\xc4\xfb\x80" + - "\x09t\xe5]8\xa8\xda\x93\xb4\x98\xf6\xffZ\xb8\xff\xe9" + - "\x99\xb1\xd1[\xe0\x7fo\x12\xf1\x0d\x11\xd5wb\xad\xdd" + - "\xdb\xcb\x00\xd4\xb7DT?\x89^\x94>\"G\xfdD" + - "\xc4\xdeX\x89.\x7fA\x8c\x9fS!\x1bOX5\xf8" + - "\x00@\xb6\x96\x12D\x9a'\xac\x84\x97\xb0d\x1c\x04\xc8" + - "\xa6\x88>=^\xa07\xe1\x00@\xb6\x91\xe8\xb3\xd1\xef" + - "\xc8\x83\xd7\xa8\x8a\x15\x81{\xd1,,\xd7\x8dq\xab\xbe" + - "\xe0\x89\x0b\x1d\x82\xcc\x8aE\xb8?\x16_\xbb\x17\xc7\xea" + - "\xe0p\xec\x84\xcc\xcaR\x88\xe7\xd1\x0e\xc79W1\xfd" + - "\x9d\xc0\x1cY?\xf8\xbc\xd8\xf3k\x89\xd80\xe0Xl" + - "N\x16\x18C}\xce\x9f?\xdd\x1d3\xc6w\x06\x01\xd4" + - "\xd5\"\xaa\xc3\x02\x07(\xb3\xbf\x9c\xd7\xd0aK-\xb6" + - "\xae\xc2$#7\x125\xc5\xd4\x16\xe6\xec~,SA" + - "\xbe\xd4b\xad\xeb*,\xce\x10\xbcv\x80\xa4\x9b\xf9\x8b" + - "\x9e9\xc6\xa9,og\x83Y3\xb7\x969c^\x81" + - "\xaa^*{\xa3\xa7\x8e\xf0\xa1\xb27\xfeP\xe9\xc3\xda" + - ":r\xf0\xb2\x88\xea\xe6\x18\xac\x8d\x8cF\x1d\xf5\xf8\xa5" + - "\xc4\xffL\xf6\xffJ\x8fuTHKWRd\x86\x7f" + - "D\xf4\x15\x07\xfdW\xda\x13DO\xd0W9\x1c\x83\x10" + - "k0\xf6'&t\x88\xe0o\xfe_\x01\x00\x00\xff\xff" + - "\x86\xbe\xf5t" +const schema_db8274f9144abc7e = "x\xda\xccZ{\x90\x15ev?\xa7\xfb\xde\xe9\x19`" + + "\xb8\xb7\xed\xb1\x18X`\x041+\xac\x10u\xd6\xc4\x9d" + + "$;O\xd8\x19\x16az\xee\x0c\xba#\xa6\xec\xb9\xf7" + + "\x9b\x99\x86\xbe\xdd\x97~\x00CpA\x0a\xa2L`\x05" + + "\x17R\xe0\xe2\x16\xe0\x12\x1fq\xb3\xe2be5jI" + + "\xb2\x1b$\xabQ6\x98\x92\x8dVT\xb0Rk\xad\xe5" + + "\xa2\xa6,Sj\xa7N\xbf\xe7\xce0\x03k\xfe\xc8?" + + ":u\xee\xe9\xefq\x1e\xbf\xf3;\xe7\xe3\xfa\x9d\x93\x9a" + + "\xb8\x1b\xd2'\xab\x01\xe4C\xe9\x0a\x97-\xf8\xd5\xc6C" + + "\xd7\xfc\xd3V\x90g \xba\xdf}vi\xcd\xa7\xf6\xd6" + + "\xff\x804/\x00\xd4\x9f\xaa\xd8\x88\xd2\x9b\x15\x02\x80t" + + "\xb6\xe2\xbf\x00\xdd\xf4\x1f\xbc\xf6V\xe9-a\x1b\x883" + + "\x92\xca\x1c)?',E\xe9\xb4@\xca/\x0b\xeb\x01" + + "\xdd?-\xber\xe4\x8f\xf6\xfd\x92\x94\xb9X\x19\xb0\xfe" + + "\xa6\xca\x8d(uT\x92\xe6\xe2\xca\x15\x80\xeeG{k" + + "\xff\xf6\xf0\xbf\xbe\xb8\x1d\xc4\xaf\"\x04{\xdfQ\xf9k" + + "\x04\x94\xd6V\xfe\x04\xd0\xfd\xb7\xeb7\x9d\xbf\xf3\xa3=" + + "\xf7\x8e\xdc7Ezb\xd50J\xf3\xab\x04\xe0\xdd\x07" + + "o\xaf\xf9\x17<\xf4\xc9\x1e\x10\xaf\xa5e\x90~NW" + + "M\xe2\x00\xa5YU\x8d\x80\xee+\xd7=\xfb\xcc\xee\x9f" + + "\xde\xf3\x03\x90\xbf\x8a\x08\xfe\xf7\x7fV\xf5?\xb4\x8f\xec" + + ")\\\xf8\xd1\xd7R?~\xe5\x8a\x1fz\x0a\xee\xd1\xd3" + + "\xb7>\xb9\xfb\xa7W\xbd\x07=\x9c\x80)\x80\xfa\xa1*" + + "\x93t\xb7W\x91-\xf6\xbe\xfe\xdc\xf2\xe2\x9e\x07\x8e\xf8" + + "\x87\xf6\xd6b\x938\x0eR\xee\xb6\x8eO\x8a=\x0f\xe5" + + "\x1e\x0a\xae\x93\xa6\x9f\xbe3\xe9C\x04\xac/N\xaaC" + + "@\xf7\xa6_\xbf\xbb\xe2\x96'\xfb\x1f\x0e4\xbc\x93\xee" + + "\x98\xbc\x91Nzt2\x1d\xe4\xc5\xf5\xd9\x9d\xcd\x7f|" + + "\xdf\xc3\xe5n\xf1\xd6:5y\x18\xa5\xb7'\xd3\x9fo" + + "N\xbe\x95\xd6\x1b\xfe\xc6s+?\xfaK\xeb1\x90\x17" + + "b\xca\xfd\xf9\x8es\xeb\xe6?\xda\x7f\xd2;7\x0fP" + + "\xdfS\xfd+:\xb7ZM\xb6\xac\xfe\x87\x05\xcb\xef;" + + "\xbf\xec\x18-\x9d\xf0\x8b\x7f\x88\xaa\xa9\x0d(M\x9fJ" + + "\xae\xb9r*i\xbfz\xdd\xca\xe7\x9f\x7fb\xe0X\xf9" + + "A<\x97?5u)J\xa7<\xed_x\xdaWv" + + "\xe0\x1b/\xdc\x90\xfa\xfb\xa4#\x872\xef\xd1\xe6\xbb2" + + "\xa4p\xfbgO\xfd\xe3\xe2\x0f\xce<\x9dt\xd1\xc2," + + "G\x17o\xce\xd2\xc5{\x87\xb1\xf8FC\xd3\xf3 _" + + "\x8b\xe8\xae\xde\xb7\xc9n\xdf\xbf\xcb\x85\x1e\x14\x90\x03\xa8" + + "W\xb3\x1bi1'K\x016\xeb\xfd\x96j\xfd\x83\xad" + + "/\x94E\xa3\xb7\xeb\xe9\xecR\x94\xde\xcd\xd2\xd1\xde\xce" + + "\xfe\x04\xf0\x93\xc7\xee\xd9\xddq\xae\xed\xa4<\x03S\xe5" + + "\x97\xbeK\xdc\x88\xd2\x1e\x91\xfe\xdc%z\xfe\x89,X" + + "\xa6\xee\x07\xfa\x15\xabQ:}\x05\xfd\xf9\xf2\x15\x9e\xfa" + + "\xd2\xdb\xbf\x7f\x7f\xfa\xdd\xef\x9f,7)\x85x\xfdY" + + "\xc9D\xe9}\x89\xfe\xfc\x8d4\x8d\x07tg<\xf1'" + + "\x7f\xd7R8\xfb\xcb1\xb2HR\xa7}(9\xd3\xe8" + + "\xaf\xb5\xd3\xe8\x8e\xe7\x16\x1e\xfb\x8b\xdf\xec:}&\x19" + + ")/O\xf3B\xf6\xedid\xb0{\xbe6\xb4q\xf9" + + "5\xc3\xaf\x95;\xc8\xd3\xc4\xdaa\x94\xa6\xd7z\xee\xac" + + "\xa5\xe5\xb8w\x95\xe9[\xfe\xfd\x9bo$\x82v{\xed" + + ";\x08)w\xf9\xca\xdbWW\xddu\xee\\r\xa3\xa1" + + "Z\xdfu\xb5\xb4\xd1q\xf1~\xe9\xd9\xc3\x7fs\x9e6" + + "\x12\xca\xcd\xfd\xe3\xda^\x94N\xd4z\xe6\xa9}\x98\x83" + + "D\xf2\x8c\x158O\x7f\xa5\x01\xa5S_\xf1\x02\xe7+" + + "t\xae\x9b\xeelf\xabn\xbe\xed=\x10g\xf0#\xb0" + + "\"=\xb3\x01\xa5+gz\x99>\xf3\x1e\x94\xe6\xcf\x12" + + "\x00\xdc\xef\x0d\xf4\x9e\xba\xd0z\xf8w\xe5\x8b\xfb\x880" + + "\xab\x01\xa59\xa4W?k\x96\xe7\x9f\xfa\x1b\xfe\xea\xfd" + + "}\x0f\xb5^\x18\xb5\xba:\xbb\x05\xa5\xa1\xd9t\x0eg" + + "\xf6\xb7\xa4\xa3\xb3\xbd\xc5\xbf\xdb\xb6\xe2\x1bsO|\x98" + + "\xb4\xc4\xae\xd9\x94\xbe\xd2\xe1\xd9d\x89\xfe\x9b\x7f\xfb\xad" + + "k\xbe\xf7\xcf\x1f\x96\xf9\xcfS<1{\x01J\xa7\xbd" + + "\x15_&\xe5\x0f\x96\xfc\xf0\xcc\x8c\xcc\x8c\x8f\xcb\x0eJ" + + "\x98Z\x7fa\xf6j\x94\xd2u\x9e\xa3\xeaN\xd2Ao" + + "{\xe7\x81\xf5\x8d?\xf8\xf8\x13\xba\x17_\x06t\xff}" + + "U/JUsh\xe5\xf4\x1c\xca\xa5e\x8f\x9f\xfd\xe6" + + "\xe0\xbe\x17?\x1d\x13\xba\x8f\xcd\xd9\x8a\xd2/<\xed\x13" + + "s\x08\xae\xfeZ8xn\xcb\x7f\xfe\xf9g\xc9[=" + + ":\xf7\x1d\xba\xd5ss\xe9V\x9b>8\xd0~\xdf\xaa" + + "\xc7\xbfH*\xbc9w+\xa5\xe6\xa7\x9eB\x94\x8cc" + + "E\xda\xf4\xab[P\x9a\x7f5\xedw\xcd\xd5\xa4m;" + + "\xba\xce4\xb3\x94\xca\xffa\xf8g~Q^)\xe9\xa5" + + "\x86f\xc7\x1ed\xba\xad\xe6\x15\x9bu\xb1F\xabd\xe8" + + "\x16\xebD\x94\xb3|\x0a \x85\x00\xa2\xb2\x1a@\xbe\x93" + + "GY\xe3PD\xac!\xb8\x16U\x12\x0e\xf2(\xdb\x1c" + + "\x8a\x1cWC\x88 \xae\x9d\x0b k<\xca\x1b8D" + + "\xbe\x86\xf0Nt\xee\x07\x907\xf0(o\xe3\xd0-1" + + "\xb3\xa8\xe8L\x87\x8c\xbd\xd84q\x0ap8\x05\xd05" + + "\x99m\x0e)}\x1adXB,\xac^oc5p" + + "X\x0d\xe8\x0e\x1a\x8ei\xf5\xe86\xaaZ\x17\xeb7\x99" + + "\x85\x83X\x01\x1cV\x8c\x7f\xbdVC\xd7Y\xde\xce9" + + "\xf9<\xb3,\x00\xbaYet\xb3\xf9\x0f\x00\xc8\xd7\xf1" + + "(\xdf\x9c\xb8\xd9Mt\xb3\xaf\xf3(7q\xe8Z\xcc" + + "\\\xc7\xcce\x06\xe6\x15[5\xf4\xe5\x0a_d\xd1\xb1" + + "\xf3\x9a\xcat\xbb\xd5\x80\x8c\xde\xaf\x0e`6N\x05@" + + "\xcc\x8e\x7f\xb0\xc5\x1bT\xcbV\xf5\x81nO\xde\xd8i" + + "hj~\x88N7\xc5\xb3\xe4\xac\x06ZC\xbc\xb2\x17" + + "\x009Ql\x01hT\x07t\xc3dnA\xb5\xf2t" + + ")\xe0\xf3\xf6\xe6>ES\xf4<\x8b6\xaa\x18\xbd\x91" + + "\xbfA\xce\xbb\xc7\"%\xe1\xedy\x9d\x8a\xa9\xf0EK" + + "\x9e\x12\xd9cq/\x80\xdc\xc6\xa3\xdc\x99\xb0\xc7-K" + + "\x01\xe4e<\xca\xb7%<\xdd\xd3\x02 w\xf2(\xaf" + + "\xe2\xd05Lu@\xd5[\x19\xf0f\xd2a\x96\xad+" + + "E\x06\x00\xa1\xc16\x1b%2\xa2\x85\xd9\x18\xa5\xcb," + + "\x95\x1e}\x81v\xa6i\xc6\xad\x86\xa9\x15V\xf8\xfb\x18" + + "dm\xcf\x95\xd1g\xc2\x18\x9e\xf7\x9cC\xf7V\xf3l" + + "\x91c1\xff;\xc7\xf4\x1c9\xaf\x8bY\x8ef[\x00" + + "r*\xba~u\x03\x80\\\xc9\xa3\\\xc3a\xa3\xe9)" + + "`6\x06\xf5\xb2\xa3NdkG7\xd9\x80j\xd9\xcc" + + "\xf4\xc5\xf3\x1a\xc9\xe0E+\xb9!\xc5_\x96Gy&" + + "\x87\xee\x80\xa9\xe4Y'3Q5\x0a\xcb\x15\xdd\xc8\xf1" + + ",\x8fi\xe00=~$-QT\x8d\x15\xfc\xdb-" + + "\xca\xd7y\xff\xa7\xec\x9d\xe2\xba~\xfa\xf6\xc6\xe9[\x8d" + + "_\xb8A\xfen\x8c\xf3\xb7\x9a\xfb\xdc\x1d\x9d\xc0\xd5\xfc" + + "gn\x90\xc2\x94\x116\x8f\xf2\x16\xca\x08\xa7D6\xb5" + + "\x807L\xcc\xc6(\x19X\x87\x15\x06\xc8\xd2:4\xb2" + + "<\x19\x1a\xb3a\xb5\xf7\x15\x84\x821\x88\xd9\x98\xca\x04" + + "\x9f\x99l\x1d3-\xd6\x09\x19\xd3\xd80\x84\xd9\xb8\xea" + + "\x97Y}\xea\xe5Z=tt\xf4\xd5\xf8\xdf\x9b,\xef" + + "CF\xf0yg\x9d\xef\xb4\x9a\xc8iw\xcd\x8d\x01-" + + "J\x92\xbb\xfb\x00\xe4-<\xca;\x13I\xb2\x83,\x7f" + + "/\x8f\xf2^\x0eE>\xc0\xc3=\x94N\xbby\x94\x0f" + + "r(\xa6R5Df\xc5\x03\x94N{y\x94\x0fq" + + "#a\x8f\xadc\xba\xdd\xa6\x0e\x80\xc0\xacXJGl" + + "S\x07\x18\xf0\xd6\x97M\xb8\xca\x09\xeca\xf4Y\x86\xc6" + + "l\xd6\xc6\xf2\x9aB\xb9\xb3\x8e\xf9\xbf\x07\xc8:VT" + + "\xd3\xb5\xa7\xf0(\xd7\x12\xe2\xd3\xaf\xcc&l\xa0cD" + + "\\z\xe2\xbco\xa5\xff\x06\xbbt\x06\xab\x98\x01\x84\xd7" + + "F\x9b\x1d\xa0\xcd\xf6\xf3(\xff(\xe1\x8d\xc3&uT" + + "<\xca\x8fs\x88\x813\x1e=\x02 ?\xce\xa3\xfc3" + + "r\x06\xe7;\xe3\xa9\x05\x00\xf2\x13<\xca/\x913x" + + "\xdf\x19\xa7(+_\xe2Q~\x9dC1\x9d\xaa\xc14" + + "\x80\xf8\x1a9\xf8\x0c\x8f\xf2[\x17\x03\xbc\xbcf8\x85" + + "~M\x81:\x93\x15:\xda\"\xb9\xee\x14;M\xb6N" + + "E\xc3\xb1\x9am\x9b\x15\x85\x92m\x85\xb5+c+\x03" + + "\x16N\x05\xec\xe4\x11\xb31\x1b\x05$a\xb4&\x9a\xac" + + "\xb0\x92\x99\x96\xca\x1bzT~T\xddf\xba\xbdL\x01" + + "\xa1\x8fi\x91t\x1cx\xea\x0a\x92\x8cR,\xc0\x0b#" + + "\x86T\x1c\xa0J0\xd3u\x03#.&\xdb4\xf1(" + + "/\xe3p\x16~Ab\xb2cG\x17\x80\xdc\xce\xa3\xdc" + + "\xcd\xe1,\xees\x12\x93%\xe5\xde\xb8 d\x06m\xbb" + + "\x84\xd9\x98\xa5\x06\xce^\xcf\xfa,#\xbf\x86\x01\x12\xae" + + "F\x94)\xf8u0\xc0y\xe0\xb5\x02f\xe36\xb3," + + "R\xf8\x8b\x15\xf9F\xa2\x14\x86\xe9\xd5\xd0\xb8\xa2\xdd\x18" + + "_\"\x8c\x8e\x8e\xde\xf8\x06\"\xd7\xe4_K\xee\x8b\xcf" + + "_\x97W\x1c\x8b\x8dd'\xcd\xfd6\xf0\xcc\x8c\x00\xd9" + + "\x1a4\x1c\xad\xd0\xc5@\xb0\xcd!D\xe0\x10\xc7\x87\xe9" + + "6\xa3=ax?\x8c\xc7\xae\xbcQ\xe1\xedM\x16\xde" + + "\xc0\xfc=d\xfen\x1e\xe5\x12\x87\xaeF@\xa7\xb7\x1b" + + "\x1e\x0e\x84\xc7\xf5\x85\x9d\x86\x17\x9c\x02p(\x00\xbaN" + + "\xc9\xb2M\xa6\x14\x01\xa3h#\xfd\xa9\x97Q\xcf\xcap" + + "\xb5S\xc9xy\xff\xff\x89=\\>\x0d\xf0\xc1k\x04" + + "\x098\x92\xa8\xc9\xf9\xe0k\xf4>o5t\xe1\xb2\x89" + + "^\x80`~\x19Z\x14\xd0\x0a\xe2\xa0a}\x9eO\xf5" + + "d\x1e\x8f\xf2\xf5\xc9\xfa\xbc\x90Lt-\x8f\xf2\xd79" + + "\x14\x98I\xa56\x1a\x16\xf8\x9bn\xb6|R\x8b\xd9x" + + "\x144\xf1q\x12|_5\xf4Qa87N\x97\xc8" + + "\x85\x1d7&\xfc\x1a\xba\xf0\x96\xbe\xd8\xaf\xc2\x1a6\x14" + + "z\xa9\x8e\x15\x155F\xa3\xc0\xb9\xcd |;\xd6\x19" + + "\x97\x17\x07\xfc\xc1g\x0f\x8d\xbe\xb7\xe8\x90\x89\x02<\x9c" + + "\xa8\xb5\xe1!wP\x9b\xb1\x93Gy\x7f\xe2\x90\xfbZ" + + "\x12\xb56,\xc0\x07\xc8\xc1\x07y\x94\x1f\xe1\x10\x83\xfa" + + "{\x94 \xff\x11\x1e\xe5\xe3\x9c\x07\xd8\xed\xcd\xad\x86\x8e" + + "\xc1!,\x80\xa8\xd5\x18d\x8ai\xf71\x05\xed\x0e\xdd" + + "f\xe6:\x05\xb5\x10\x126\xdbj\x91\x19\x8e\x1dAD" + + "Q\xd9\xe0q3,\xb4\xfb_\x09\x8ama\x15pX" + + "E\x19i1\xb3\xd5d\x05$o(Z\xa7\xc2\xdb\x83" + + "\x97b\xa0\x91 \x9e\x19\xc3<\xc4\xec6\xf1(\xdfK" + + "P\x82\x89\x81\x94\xb8}5p\x1e\x92\xd0\x9d\xd7\xb6\xc4" + + "\\\xcf+\x88\xe9\xb2n\xcd+\x88\x15Dn\xc8:\xdb" + + "x\x94ws\xe1\xd1\xda\x0dh\xf43\xb4\xdc\xd5A7" + + "\xb4\x99PSe\xf1}\x03Z\xa5\xa2\xa1w{\x86\xc2" + + "\xd8Ry\xa3X2)\x94UC\x97\x1dESy{" + + "(\xfap\\[\x10$\xf9\xa9\xbc\xa2T\xe79\x8b\x8c" + + "qsh\x0ci\x08\x97\x02\xe46 \x8f\xb9m\x18\x87" + + "\x8bt7\xb6\x00\xe46\x91\xfc^\x8c#F\xda\x8e3" + + "\x00r[H\xbe\x13\xa3&V\xda\x81\x8f\x01\xe4v\x92" + + "x?\xc6TA\xda\xe7-\xbf\x97\xe4\x870f\x0b\xd2" + + "\x83\xb8\x00 \xb7\x9f\xe4\xc7I^\xc1y\x96\x94\x8e\xe1" + + "j\x80\xdc\x13$\x7f\x96\xe4B\xba\x86:v\xe9i4" + + "\x01r?#\xf9\xcfI^Y[\x83\x95\x00\xd2\x09O" + + "\xfe\x02\xc9_\"y\xd5\xf4\x1a\xac\x02\x90N\xe1V\x80" + + "\xdc\x8b$?C\xf2IX\x83\x93\x00\xa4\xd3\xf8\x00@" + + "\xee\x0c\xc9\xdf\"\xf9\xe4\x8a\x1a\x9c\x0c \xbd\xe9\x9d\xe7" + + "u\x92\x9f'\xf9\x94T\x0dN\x01\x90\xde\xc6#\x00\xb9" + + "\xf3$\xff\x1d\xc9\xab\x85\x1a\xac\x06\x90\xde\xf7\xee\xf5[" + + "\x92Wr\x11\x0ev\x14\x92pLa\xa8\xc6t\x847" + + "\xac(\x14X\xd0\xdc\xa2_+:\x8d\x0cu\xb7\x98\x89" + + "\xc7\xd0\x80\x98\x01tK\x86\xa1-\x1f\x09\xf3\x131\xa2" + + " \x8c c\xe8\x1d\x85(/\xfd\xe0[f@]^" + + "\xd1:J1G\xb2\x9a\x1d\xdbpJPWPlV" + + "\x88\x0a\xb5\xe9\xe8KL\xa3\xd8\x8d\xcc,\xaa\xba\xa2A" + + "\xf4\xcbx\xb1\x98q\x1c\xb5\x10\xad=.\xb1s\xfb\x99" + + "b;&\xb3\xe8j\x17\xa9\xb8\\yD\xd7\x95\x1a\xba" + + "\x95\x81\xb2\x19\xc5\x82\xb8\xde1o\xf9\x02\xc9K\x1e" + + "\xde\xa1\x8fwEo}\x8d\xe4\x1b\x92x\xe7\xe0\xf0\x08" + + "\xf8\x15x\x1f\xef\xee\xf6pj\x1b\xc9w{x\x97\xf2" + + "\xf1n\x17>\x09\x90\xdbM\xf2\x83\x1e\xde\xa5}\xbc;" + + "\x80\xcf\x00\xe4\x0e\x92\xfc\x11\x0f\xef*|\xbc;\xea\xe9" + + "?\x12\xe1\xec\xe4\x16\x1f\xef\x8ey\xf8\x18\xe1\xac\xeb\x98" + + "Z\xce6U\x1dp \xce\x8d|\xe9\xdb\x8c\x95\x9a!" + + "\xa3\xa9\xebXT\x8b\x0a\xaa\xa2\xb59\x8a\x06u9[" + + "\xc9\xaf\x89\xa9\xbdf\xb5+z\xc1\xc2Ae\x0d\xa3\x0a" + + "&$k\xbd\xadY+\x99\xa9\xf6\x03\xc6\xcd@\xc4}" + + "2\x9d\x86QN\x89*`\xfc" + + "\xcc\x82\xe1\xab\x8a\xf8\xa0\x09\x9c\xb8O@.z\x84\xc4" + + "\xf0\x01Q\xdc1\x0c\x9c\xb8]@>z\x17\xc4p\x18" + + "\x7f\xc3\xd0$\x04N\xbcK\xc0T\xf4J\x8a\xe1(_" + + "\\K\xdcJ\x150\x1d=9b\xf8^$\xde\xb1\x15" + + "8\xb1Gp\xc3\x16\x0a\x1a\xfd{4\xa1\x1b\"\x06\xd4" + + "y\x98\xd1\x84n8\xc1\xc2\xb0\xd5\x02hB7\x9c\xc3" + + "\xf0\x17\x1b\xc4xZ\xe1|\x172y\xc5fM\xd4\xbb" + + "\xfa\xc0\x8e\x01\xb2C\x13&\xe7\xa6\xfc\xc5\x1a\xa6\xb1\x89" + + "wKL\x0e\xa3\xc1\xd7p\xcc\x0d\xa3&u\xd7cI" + + "\xde\x1d\xccZ\x0el\x0d&5\xc7\x13\xb3\x96cD\xc6" + + "\x8f\xf3(\xbf\xca\xc5\x8c\"\x0c\xf8p\xd4\x88\x86\x19v" + + "\xcd\xe3L\x1c\x83\xb4\x088q\xf9\xdc\xd1-\x18\x83\x1e" + + "gF\x7f)\x0b\xe2Z\x91\x1cFNM\x0c#1\xec" + + "\xd7\x85\x11\xa5%9\x9a\x9c:A\xf3\x97\xec>\xbdZ" + + "\x97\xf2\xe25|z\xc5\xf0\x99\\\x14)\xee\xaa\x057" + + "\xecP1,\x94P\xe6\xb2\xcbl\xd3\xbbX\xdd\x97\xa9" + + "\xe4c\x04\x88\xbfO\x86\xa2\xd5\xbfP\xb4\xee\xea\xc4\xdc" + + "O3\x82\x0e3\xb3<\xd9$\x8cc+\xff\xc0!\xa5" + + "\xcf\xd0\xc7\xb4\xfeU\xd1\xfa\xa7\xe7&\xe6ra\xfc\xbd" + + "F\xc2Wy\x94\xdfH\xf4}g\x97\x02\xc8\xaf\xf3(" + + "\x7f\x1c\xbfC]\xa0@\xfd\x98\xc7\xae\x04\x7f\x17?'" + + "\xc5\xcf\x88\xe5&\xabY\x1a\xef\x07\xc8UR\xf5\xa8\xf1" + + "\xaaY\xca\xaff\"\xf6\x01\xe4\xb2$\x9f\x99d\xef\xd3" + + "\xb1\x17 WK\xf2y\x18\xb4\xeb\xe1\x1b\x96c\xc6\xc8" + + "\xaf\x19\x03\xcbT}LJ\x18>\x8c\xa1Mx\xea\x98" + + "T\x14F\x82oG[\x82$G3)df\x8eR" + + "\xbc\x80V4\xeb\xb9\x8c\x99\xf18\xee\xc8\x05\xc9\xe7\xe7" + + "^@4\x12\x93\x82#\x89!Z\xe8\x0c\xf9\x99`8" + + "ug\xc2\x19w\xf4\x01\xc8\xabx\x94\x079\x0f\xa0\x8c" + + "\x9eRAA\x9b-1\xd9Z\x87\x09z~(\xee\x98" + + "\xa9g\xcc[=X\"\xb6\xbe\xc4d\x8dk\x1d\x96T" + + "\x08\xdfH@P\x8d\xc2\xa8\xc7\x911h\xe7\xad\xac/" + + "g\xe4\xd70{\xc4\xdbQ\xd9\xfbfW\xfc@\x12=" + + "ov%\x9f7\x03X[K\x01^\xe2Q\xde\x94\x80" + + "\xb5\xa1\xe1\xb8\xdd\x1e\x9bg\xfc\xdfP\x83\xdf\xeb\x89\x8f" + + "X\xb6p)\x0c4\xfa\xa7Ge\xf9_\xf5e\x9f\x07" + + "\xc2\xe7\x97\x09O\x10\xbdh_\xe6H\x0d\"\x10\xc2\xc4" + + "\xbfX\xa1M\xb8`\xf1\xff\x0d\x00\x00\xff\xff\xcb\x86\x19" + + "\x8c" func init() { schemas.Register(schema_db8274f9144abc7e, diff --git a/vendor/modules.txt b/vendor/modules.txt index 307f6033..edc7d64b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -299,7 +299,7 @@ gopkg.in/urfave/cli.v2 gopkg.in/urfave/cli.v2/altsrc # gopkg.in/yaml.v2 v2.2.4 gopkg.in/yaml.v2 -# zombiezen.com/go/capnproto2 v0.0.0-20180616160808-7cfd211c19c7 +# zombiezen.com/go/capnproto2 v2.18.0+incompatible zombiezen.com/go/capnproto2 zombiezen.com/go/capnproto2/encoding/text zombiezen.com/go/capnproto2/internal/fulfiller diff --git a/vendor/zombiezen.com/go/capnproto2/AUTHORS b/vendor/zombiezen.com/go/capnproto2/AUTHORS index 272a1a16..3ea1e9c7 100644 --- a/vendor/zombiezen.com/go/capnproto2/AUTHORS +++ b/vendor/zombiezen.com/go/capnproto2/AUTHORS @@ -10,8 +10,10 @@ # Please keep the list sorted. +Anapaya Systems AG CloudFlare Inc. Daniel Darabos +Dominik Roos Eran Duchan Evan Shaw Google Inc. @@ -20,8 +22,11 @@ James McKaskill Jason E. Aten Johan Hernandez Joonsung Lee +Kiwi.com s.r.o. Lev Radomislensky Peter Waldschmidt +Tiit Pikma Tom Thorogood TJ Holowaychuk William Laffin +Colin Arnott diff --git a/vendor/zombiezen.com/go/capnproto2/CHANGELOG.md b/vendor/zombiezen.com/go/capnproto2/CHANGELOG.md index ae0ec8bf..c3f67286 100644 --- a/vendor/zombiezen.com/go/capnproto2/CHANGELOG.md +++ b/vendor/zombiezen.com/go/capnproto2/CHANGELOG.md @@ -1,5 +1,10 @@ # Go Cap'n Proto Release Notes +## 2.17.3 + +- Clear read limits for `const` messages in schemas. + ([#131](https://github.com/capnproto/go-capnproto2/pull/131)) + ## 2.17.0 - Add `capnp.Canonicalize` function that implements the diff --git a/vendor/zombiezen.com/go/capnproto2/CONTRIBUTORS b/vendor/zombiezen.com/go/capnproto2/CONTRIBUTORS index bcdd5743..9a1aa311 100644 --- a/vendor/zombiezen.com/go/capnproto2/CONTRIBUTORS +++ b/vendor/zombiezen.com/go/capnproto2/CONTRIBUTORS @@ -18,6 +18,7 @@ Alan Braithwaite Albert Strasheim Daniel Darabos +Dominik Roos Eran Duchan Evan Shaw Ian Denhardt @@ -26,8 +27,13 @@ Jason E. Aten Johan Hernandez Joonsung Lee Lev Radomislensky +Lukas Vogel +Martin Sucha Peter Waldschmidt Ross Light +Stephen Shirley +Tiit Pikma Tom Thorogood TJ Holowaychuk William Laffin +Colin Arnott diff --git a/vendor/zombiezen.com/go/capnproto2/WORKSPACE b/vendor/zombiezen.com/go/capnproto2/WORKSPACE index 7b5176a9..7d3d0a61 100644 --- a/vendor/zombiezen.com/go/capnproto2/WORKSPACE +++ b/vendor/zombiezen.com/go/capnproto2/WORKSPACE @@ -4,17 +4,17 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "io_bazel_rules_go", - sha256 = "8b68d0630d63d95dacc0016c3bb4b76154fe34fca93efd65d1c366de3fcb4294", - urls = ["https://github.com/bazelbuild/rules_go/releases/download/0.12.1/rules_go-0.12.1.tar.gz"], + sha256 = "86ae934bd4c43b99893fc64be9d9fc684b81461581df7ea8fc291c816f5ee8c5", + urls = ["https://github.com/bazelbuild/rules_go/releases/download/0.18.3/rules_go-0.18.3.tar.gz"], ) http_archive( name = "bazel_gazelle", - sha256 = "ddedc7aaeb61f2654d7d7d4fd7940052ea992ccdb031b8f9797ed143ac7e8d43", - urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/0.12.0/bazel-gazelle-0.12.0.tar.gz"], + sha256 = "3c681998538231a2d24d0c07ed5a7658cb72bfb5fd4bf9911157c0e9ac6a2687", + urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/0.17.0/bazel-gazelle-0.17.0.tar.gz"], ) -load("@io_bazel_rules_go//go:def.bzl", "go_register_toolchains", "go_rules_dependencies") +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") go_rules_dependencies() diff --git a/vendor/zombiezen.com/go/capnproto2/capn.go b/vendor/zombiezen.com/go/capnproto2/capn.go index 6de4c836..c4701e6e 100644 --- a/vendor/zombiezen.com/go/capnproto2/capn.go +++ b/vendor/zombiezen.com/go/capnproto2/capn.go @@ -200,6 +200,9 @@ func (s *Segment) readListPtr(base Address, val rawPointer) (List, error) { } sz := hdr.structSize() n := int32(hdr.offset()) + if n < 0 { + return List{}, errListSize + } // TODO(light): check that this has the same end address if tsize, ok := sz.totalSize().times(n); !ok { return List{}, errOverflow @@ -214,11 +217,15 @@ func (s *Segment) readListPtr(base Address, val rawPointer) (List, error) { flags: isCompositeList, }, nil } + n := val.numListElements() + if n < 0 { + return List{}, errListSize + } if lt == bit1List { return List{ seg: s, off: addr, - length: val.numListElements(), + length: n, flags: isBitList, }, nil } @@ -226,7 +233,7 @@ func (s *Segment) readListPtr(base Address, val rawPointer) (List, error) { seg: s, size: val.elementSize(), off: addr, - length: val.numListElements(), + length: n, }, nil } diff --git a/vendor/zombiezen.com/go/capnproto2/doc.go b/vendor/zombiezen.com/go/capnproto2/doc.go index ef7f6497..f4109f1a 100644 --- a/vendor/zombiezen.com/go/capnproto2/doc.go +++ b/vendor/zombiezen.com/go/capnproto2/doc.go @@ -81,10 +81,10 @@ Structs For the following schema: -struct Foo @0x8423424e9b01c0af { - num @0 :UInt32; - bar @1 :Foo; -} + struct Foo @0x8423424e9b01c0af { + num @0 :UInt32; + bar @1 :Foo; + } capnpc-go will generate: @@ -168,9 +168,9 @@ For each group a typedef is created with a different method set for just the groups fields: struct Foo { - group :Group { - field @0 :Bool; - } + group :Group { + field @0 :Bool; + } } generates the following: @@ -194,10 +194,10 @@ Named unions are treated as a group with an inner unnamed union. Unnamed unions generate an enum Type_Which and a corresponding Which() function: struct Foo { - union { - a @0 :Bool; - b @1 :Bool; - } + union { + a @0 :Bool; + b @1 :Bool; + } } generates the following: @@ -225,10 +225,10 @@ For voids in unions, there is a void setter that just sets the discriminator. For example: struct Foo { - union { - a @0 :Void; - b @1 :Void; - } + union { + a @0 :Void; + b @1 :Void; + } } generates the following: @@ -241,14 +241,14 @@ the discriminator. This must be called before the group getter can be used to set values. For example: struct Foo { - union { - a :group { - v :Bool - } - b :group { - v :Bool - } - } + union { + a :group { + v :Bool + } + b :group { + v :Bool + } + } } and in usage: @@ -293,10 +293,10 @@ but the tags can be customized with a $Go.tag or $Go.notag annotation. For example: enum ElementSize { - empty @0 $Go.tag("void"); - bit @1 $Go.tag("1 bit"); - byte @2 $Go.tag("8 bits"); - inlineComposite @7 $Go.notag; + empty @0 $Go.tag("void"); + bit @1 $Go.tag("1 bit"); + byte @2 $Go.tag("8 bits"); + inlineComposite @7 $Go.notag; } In the generated go file: diff --git a/vendor/zombiezen.com/go/capnproto2/encoding/text/marshal.go b/vendor/zombiezen.com/go/capnproto2/encoding/text/marshal.go index 48cdaf57..fde265c3 100644 --- a/vendor/zombiezen.com/go/capnproto2/encoding/text/marshal.go +++ b/vendor/zombiezen.com/go/capnproto2/encoding/text/marshal.go @@ -42,14 +42,14 @@ func MarshalList(typeID uint64, l capnp.List) (string, error) { // An Encoder writes the text format of Cap'n Proto messages to an output stream. type Encoder struct { - w errWriter + w indentWriter tmp []byte nodes nodemap.Map } // NewEncoder returns a new encoder that writes to w. func NewEncoder(w io.Writer) *Encoder { - return &Encoder{w: errWriter{w: w}} + return &Encoder{w: indentWriter{w: w}} } // UseRegistry changes the registry that the encoder consults for @@ -58,6 +58,12 @@ func (enc *Encoder) UseRegistry(reg *schemas.Registry) { enc.nodes.UseRegistry(reg) } +// SetIndent sets string to indent each level with. +// An empty string disables indentation. +func (enc *Encoder) SetIndent(indent string) { + enc.w.indentPerLevel = indent +} + // Encode writes the text representation of s to the stream. func (enc *Encoder) Encode(typeID uint64, s capnp.Struct) error { if enc.w.err != nil { @@ -133,8 +139,14 @@ func (enc *Encoder) marshalStruct(typeID uint64, s capnp.Struct) error { if n.StructNode().DiscriminantCount() > 0 { discriminant = s.Uint16(capnp.DataOffset(n.StructNode().DiscriminantOffset() * 2)) } - enc.w.WriteByte('(') fields := codeOrderFields(n.StructNode()) + if len(fields) == 0 { + enc.w.WriteString("()") + return nil + } + enc.w.WriteByte('(') + enc.w.Indent() + enc.w.NewLine() first := true for _, f := range fields { if !(f.Which() == schema.Field_Which_slot || f.Which() == schema.Field_Which_group) { @@ -144,7 +156,8 @@ func (enc *Encoder) marshalStruct(typeID uint64, s capnp.Struct) error { continue } if !first { - enc.w.WriteString(", ") + enc.w.WriteByte(',') + enc.w.NewLineOrSpace() } first = false name, err := f.NameBytes() @@ -164,6 +177,8 @@ func (enc *Encoder) marshalStruct(typeID uint64, s capnp.Struct) error { } } } + enc.w.NewLine() + enc.w.Unindent() enc.w.WriteByte(')') return nil } @@ -298,101 +313,154 @@ func codeOrderFields(s schema.Node_structNode) []schema.Field { } func (enc *Encoder) marshalList(elem schema.Type, l capnp.List) error { - switch elem.Which() { - case schema.Type_Which_void: - enc.w.WriteString(capnp.VoidList{List: l}.String()) - case schema.Type_Which_bool: - enc.w.WriteString(capnp.BitList{List: l}.String()) - case schema.Type_Which_int8: - enc.w.WriteString(capnp.Int8List{List: l}.String()) - case schema.Type_Which_int16: - enc.w.WriteString(capnp.Int16List{List: l}.String()) - case schema.Type_Which_int32: - enc.w.WriteString(capnp.Int32List{List: l}.String()) - case schema.Type_Which_int64: - enc.w.WriteString(capnp.Int64List{List: l}.String()) - case schema.Type_Which_uint8: - enc.w.WriteString(capnp.UInt8List{List: l}.String()) - case schema.Type_Which_uint16: - enc.w.WriteString(capnp.UInt16List{List: l}.String()) - case schema.Type_Which_uint32: - enc.w.WriteString(capnp.UInt32List{List: l}.String()) - case schema.Type_Which_uint64: - enc.w.WriteString(capnp.UInt64List{List: l}.String()) - case schema.Type_Which_float32: - enc.w.WriteString(capnp.Float32List{List: l}.String()) - case schema.Type_Which_float64: - enc.w.WriteString(capnp.Float64List{List: l}.String()) - case schema.Type_Which_data: - enc.w.WriteString(capnp.DataList{List: l}.String()) - case schema.Type_Which_text: - enc.w.WriteString(capnp.TextList{List: l}.String()) - case schema.Type_Which_structType: + writeListItems := func(writeItem func(i int) error) error { + if l.Len() == 0 { + _, err := enc.w.WriteString("[]") + return err + } enc.w.WriteByte('[') + enc.w.Indent() + enc.w.NewLine() for i := 0; i < l.Len(); i++ { - if i > 0 { - enc.w.WriteString(", ") - } - err := enc.marshalStruct(elem.StructType().TypeId(), l.Struct(i)) + err := writeItem(i) if err != nil { return err } + if i == l.Len()-1 { + enc.w.NewLine() + } else { + enc.w.WriteByte(',') + enc.w.NewLineOrSpace() + } } + enc.w.Unindent() enc.w.WriteByte(']') + return nil + } + writeListItemsN := func(writeItem func(i int) (int, error)) error { + return writeListItems(func(i int) error { + _, err := writeItem(i) + return err + }) + } + switch elem.Which() { + case schema.Type_Which_void: + return writeListItemsN(func(_ int) (int, error) { + return enc.w.WriteString("void") + }) + case schema.Type_Which_bool: + p := capnp.BitList{List: l} + return writeListItemsN(func(i int) (int, error) { + if p.At(i) { + return enc.w.WriteString("true") + } else { + return enc.w.WriteString("false") + } + }) + case schema.Type_Which_int8: + p := capnp.Int8List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatInt(int64(p.At(i)), 10)) + }) + case schema.Type_Which_int16: + p := capnp.Int16List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatInt(int64(p.At(i)), 10)) + }) + case schema.Type_Which_int32: + p := capnp.Int32List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatInt(int64(p.At(i)), 10)) + }) + case schema.Type_Which_int64: + p := capnp.Int64List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatInt(p.At(i), 10)) + }) + case schema.Type_Which_uint8: + p := capnp.UInt8List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatUint(uint64(p.At(i)), 10)) + }) + case schema.Type_Which_uint16: + p := capnp.UInt16List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatUint(uint64(p.At(i)), 10)) + }) + case schema.Type_Which_uint32: + p := capnp.UInt32List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatUint(uint64(p.At(i)), 10)) + }) + case schema.Type_Which_uint64: + p := capnp.UInt64List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatUint(p.At(i), 10)) + }) + case schema.Type_Which_float32: + p := capnp.Float32List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatFloat(float64(p.At(i)), 'g', -1, 32)) + }) + case schema.Type_Which_float64: + p := capnp.Float64List{List: l} + return writeListItemsN(func(i int) (int, error) { + return enc.w.WriteString(strconv.FormatFloat(p.At(i), 'g', -1, 64)) + }) + case schema.Type_Which_data: + p := capnp.DataList{List: l} + return writeListItemsN(func(i int) (int, error) { + s, err := p.At(i) + if err != nil { + return enc.w.WriteString("") + } + buf := strquote.Append(nil, s) + return enc.w.Write(buf) + }) + case schema.Type_Which_text: + p := capnp.TextList{List: l} + return writeListItemsN(func(i int) (int, error) { + s, err := p.BytesAt(i) + if err != nil { + return enc.w.WriteString("") + } + buf := strquote.Append(nil, s) + return enc.w.Write(buf) + }) + case schema.Type_Which_structType: + return writeListItems(func(i int) error { + return enc.marshalStruct(elem.StructType().TypeId(), l.Struct(i)) + }) case schema.Type_Which_list: - enc.w.WriteByte('[') ee, err := elem.List().ElementType() if err != nil { return err } - for i := 0; i < l.Len(); i++ { - if i > 0 { - enc.w.WriteString(", ") - } + return writeListItems(func(i int) error { p, err := capnp.PointerList{List: l}.PtrAt(i) if err != nil { return err } - err = enc.marshalList(ee, p.List()) - if err != nil { - return err - } - } - enc.w.WriteByte(']') + return enc.marshalList(ee, p.List()) + }) case schema.Type_Which_enum: - enc.w.WriteByte('[') il := capnp.UInt16List{List: l} typ := elem.Enum().TypeId() // TODO(light): only search for node once - for i := 0; i < il.Len(); i++ { - if i > 0 { - enc.w.WriteString(", ") - } - enc.marshalEnum(typ, il.At(i)) - } - enc.w.WriteByte(']') + return writeListItems(func(i int) error { + return enc.marshalEnum(typ, il.At(i)) + }) case schema.Type_Which_interface: - enc.w.WriteByte('[') - for i := 0; i < l.Len(); i++ { - if i > 0 { - enc.w.WriteString(", ") - } - enc.w.WriteString(interfaceMarker) - } - enc.w.WriteByte(']') + return writeListItemsN(func(_ int) (int, error) { + return enc.w.WriteString(interfaceMarker) + }) case schema.Type_Which_anyPointer: - enc.w.WriteByte('[') - for i := 0; i < l.Len(); i++ { - if i > 0 { - enc.w.WriteString(", ") - } - enc.w.WriteString(anyPointerMarker) - } - enc.w.WriteByte(']') + return writeListItemsN(func(_ int) (int, error) { + return enc.w.WriteString(anyPointerMarker) + }) default: return fmt.Errorf("unknown list type %v", elem.Which()) } - return nil } func (enc *Encoder) marshalEnum(typ uint64, val uint16) error { @@ -419,37 +487,96 @@ func (enc *Encoder) marshalEnum(typ uint64, val uint16) error { return nil } -type errWriter struct { +// indentWriter is helper for writing indented text +type indentWriter struct { w io.Writer err error + + // indentPerLevel is a string to prepend to a line for every level of indentation. + indentPerLevel string + + // current indent level + currentIndent int + + // hasLineContent is true when we have written something on the current line. + hasLineContent bool } -func (ew *errWriter) Write(p []byte) (int, error) { - if ew.err != nil { - return 0, ew.err +func (iw *indentWriter) beforeWrite() { + if iw.err != nil { + return + } + if len(iw.indentPerLevel) > 0 && !iw.hasLineContent { + iw.hasLineContent = true + for i := 0; i < iw.currentIndent; i++ { + _, err := iw.w.Write([]byte(iw.indentPerLevel)) + if err != nil { + iw.err = err + return + } + } + } +} + +func (iw *indentWriter) Write(p []byte) (int, error) { + iw.beforeWrite() + if iw.err != nil { + return 0, iw.err } var n int - n, ew.err = ew.w.Write(p) - return n, ew.err + n, iw.err = iw.w.Write(p) + return n, iw.err } -func (ew *errWriter) WriteString(s string) (int, error) { - if ew.err != nil { - return 0, ew.err +func (iw *indentWriter) WriteString(s string) (int, error) { + iw.beforeWrite() + if iw.err != nil { + return 0, iw.err } var n int - n, ew.err = io.WriteString(ew.w, s) - return n, ew.err + n, iw.err = io.WriteString(iw.w, s) + return n, iw.err } -func (ew *errWriter) WriteByte(b byte) error { - if ew.err != nil { - return ew.err +func (iw *indentWriter) WriteByte(b byte) error { + iw.beforeWrite() + if iw.err != nil { + return iw.err } - if bw, ok := ew.w.(io.ByteWriter); ok { - ew.err = bw.WriteByte(b) + if bw, ok := iw.w.(io.ByteWriter); ok { + iw.err = bw.WriteByte(b) } else { - _, ew.err = ew.w.Write([]byte{b}) + _, iw.err = iw.w.Write([]byte{b}) + } + return iw.err +} + +func (iw *indentWriter) Indent() { + iw.currentIndent++ +} + +func (iw *indentWriter) Unindent() { + iw.currentIndent-- +} + +func (iw *indentWriter) NewLine() { + if len(iw.indentPerLevel) > 0 && iw.hasLineContent { + if iw.err != nil { + return + } + if bw, ok := iw.w.(io.ByteWriter); ok { + iw.err = bw.WriteByte('\n') + } else { + _, iw.err = iw.w.Write([]byte{'\n'}) + } + iw.hasLineContent = false + } +} + +func (iw *indentWriter) NewLineOrSpace() { + if len(iw.indentPerLevel) > 0 { + iw.NewLine() + } else { + iw.WriteByte(' ') } - return ew.err } diff --git a/vendor/zombiezen.com/go/capnproto2/go.mod b/vendor/zombiezen.com/go/capnproto2/go.mod deleted file mode 100644 index cecb1410..00000000 --- a/vendor/zombiezen.com/go/capnproto2/go.mod +++ /dev/null @@ -1,6 +0,0 @@ -module "zombiezen.com/go/capnproto2" - -require ( - "github.com/kylelemons/godebug" v0.0.0-20170820004349-d65d576e9348 - "golang.org/x/net" v0.0.0-20180218175443-cbe0f9307d01 -) diff --git a/vendor/zombiezen.com/go/capnproto2/go.sum b/vendor/zombiezen.com/go/capnproto2/go.sum new file mode 100644 index 00000000..0f3f0297 --- /dev/null +++ b/vendor/zombiezen.com/go/capnproto2/go.sum @@ -0,0 +1,8 @@ +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= +github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01 h1:po1f06KS05FvIQQA2pMuOWZAUXiy1KYdIf0ElUU2Hhc= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/vendor/zombiezen.com/go/capnproto2/internal/packed/packed.go b/vendor/zombiezen.com/go/capnproto2/internal/packed/packed.go index 38573501..5cd2c120 100644 --- a/vendor/zombiezen.com/go/capnproto2/internal/packed/packed.go +++ b/vendor/zombiezen.com/go/capnproto2/internal/packed/packed.go @@ -142,6 +142,9 @@ func Unpack(dst, src []byte) ([]byte, error) { dst = allocWords(dst, int(src[0])) src = src[1:] n := copy(dst[start:], src) + if n < len(dst)-start { + return dst, io.ErrUnexpectedEOF + } src = src[n:] } } @@ -281,22 +284,22 @@ func (r *Reader) ReadWord(p []byte) error { switch tag { case zeroTag: z, err := r.rd.ReadByte() - if err == io.EOF { - r.err = io.ErrUnexpectedEOF - return nil - } else if err != nil { + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } r.err = err - return nil + return err } r.zeroes = int(z) case unpackedTag: l, err := r.rd.ReadByte() - if err == io.EOF { - r.err = io.ErrUnexpectedEOF - return nil - } else if err != nil { + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } r.err = err - return nil + return err } r.literal = int(l) } diff --git a/vendor/zombiezen.com/go/capnproto2/mem.go b/vendor/zombiezen.com/go/capnproto2/mem.go index e3ee869a..11f84be5 100644 --- a/vendor/zombiezen.com/go/capnproto2/mem.go +++ b/vendor/zombiezen.com/go/capnproto2/mem.go @@ -360,7 +360,7 @@ func (ssa *singleSegmentArena) Allocate(sz Size, segs map[SegmentID]*Segment) (S if hasCapacity(data, sz) { return 0, data, nil } - inc, err := nextAlloc(int64(len(data)), int64(maxSegmentSize()), sz) + inc, err := nextAlloc(int64(cap(data)), int64(maxSegmentSize()), sz) if err != nil { return 0, nil, fmt.Errorf("capnp: alloc %d bytes: %v", sz, err) } diff --git a/vendor/zombiezen.com/go/capnproto2/pointer.go b/vendor/zombiezen.com/go/capnproto2/pointer.go index 9b69e6b3..6aa7ea8d 100644 --- a/vendor/zombiezen.com/go/capnproto2/pointer.go +++ b/vendor/zombiezen.com/go/capnproto2/pointer.go @@ -155,7 +155,7 @@ func (p Ptr) text() (b []byte, ok bool) { // Text must be null-terminated. return nil, false } - return b[:len(b)-1 : len(b)], true + return b[: len(b)-1 : len(b)], true } // Data attempts to convert p into Data, returning nil if p is not a diff --git a/vendor/zombiezen.com/go/capnproto2/schemas/schemas.go b/vendor/zombiezen.com/go/capnproto2/schemas/schemas.go index 8da117e4..70a174cb 100644 --- a/vendor/zombiezen.com/go/capnproto2/schemas/schemas.go +++ b/vendor/zombiezen.com/go/capnproto2/schemas/schemas.go @@ -112,7 +112,7 @@ func (r *record) read() ([]byte, error) { } p := packed.NewReader(bufio.NewReader(z)) r.data, r.err = ioutil.ReadAll(p) - if err != nil { + if r.err != nil { r.data = nil return }