From 4858ce79d0869ccdccc3036e650b1eb1425edc26 Mon Sep 17 00:00:00 2001 From: Chung-Ting Huang Date: Thu, 20 Jun 2019 11:18:59 -0500 Subject: [PATCH] TUN-1977: Validate OriginConfig has valid URL, and use scheme to determine if a HTTPOriginService is expecting HTTP or Unix --- cmd/cloudflared/tunnel/cmd.go | 30 +- originservice/originservice.go | 63 +- streamhandler/request.go | 4 +- streamhandler/stream_handler.go | 2 +- streamhandler/stream_handler_test.go | 11 +- .../tunnelhostnamemapper_test.go | 9 +- tunnelrpc/pogs/config.go | 184 +---- tunnelrpc/pogs/config_test.go | 76 +- tunnelrpc/tunnelrpc.capnp | 39 +- tunnelrpc/tunnelrpc.capnp.go | 709 ++++++------------ 10 files changed, 375 insertions(+), 752 deletions(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 4550acfc..189af390 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -452,34 +452,30 @@ func defaultOriginConfig(c *cli.Context) (pogs.OriginConfig, error) { 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"), - ProxyConnectTimeout: c.Duration("proxy-connection-timeout"), - ExpectContinueTimeout: c.Duration("proxy-expect-continue-timeout"), - ChunkedEncoding: c.Bool("no-chunked-encoding"), + 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.URL = &pogs.UnixPath{Path: unixSocket} + originConfig.URLString = unixSocket } originAddr, err := config.ValidateUrl(c) if err != nil { return nil, errors.Wrap(err, "error validating origin URL") } - originURL, err := url.Parse(originAddr) - if err != nil { - return nil, errors.Wrapf(err, "%s is not a valid URL", originAddr) - } - originConfig.URL = &pogs.HTTPURL{URL: originURL} + originConfig.URLString = originAddr return originConfig, nil } diff --git a/originservice/originservice.go b/originservice/originservice.go index 5e7fff8a..3cd0af53 100644 --- a/originservice/originservice.go +++ b/originservice/originservice.go @@ -8,6 +8,7 @@ import ( "io" "net" "net/http" + "net/url" "strconv" "strings" @@ -22,7 +23,7 @@ import ( // 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) - OriginAddr() string + URL() *url.URL Summary() string Shutdown() } @@ -30,14 +31,14 @@ type OriginService interface { // HTTPService talks to origin using HTTP/HTTPS type HTTPService struct { client http.RoundTripper - originAddr string + originURL *url.URL chunkedEncoding bool } -func NewHTTPService(transport http.RoundTripper, originAddr string, chunkedEncoding bool) OriginService { +func NewHTTPService(transport http.RoundTripper, url *url.URL, chunkedEncoding bool) OriginService { return &HTTPService{ client: transport, - originAddr: originAddr, + originURL: url, chunkedEncoding: chunkedEncoding, } } @@ -75,36 +76,36 @@ func (hc *HTTPService) Proxy(stream *h2mux.MuxedStream, req *http.Request) (*htt return resp, nil } -func (hc *HTTPService) OriginAddr() string { - return hc.originAddr +func (hc *HTTPService) URL() *url.URL { + return hc.originURL } func (hc *HTTPService) Summary() string { - return fmt.Sprintf("HTTP service listening on %s", hc.originAddr) + 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 - originAddr string - shutdownC chan struct{} + tlsConfig *tls.Config + originURL *url.URL + shutdownC chan struct{} } -func NewWebSocketService(tlsConfig *tls.Config, url string) (OriginService, error) { +func NewWebSocketService(tlsConfig *tls.Config, url *url.URL) (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(log.CreateLogger(), listener, url, shutdownC) + websocket.StartProxyServer(log.CreateLogger(), listener, url.String(), shutdownC) }() return &WebsocketService{ - tlsConfig: tlsConfig, - originAddr: url, - shutdownC: shutdownC, + tlsConfig: tlsConfig, + originURL: url, + shutdownC: shutdownC, }, nil } @@ -127,12 +128,12 @@ func (wsc *WebsocketService) Proxy(stream *h2mux.MuxedStream, req *http.Request) return response, nil } -func (wsc *WebsocketService) OriginAddr() string { - return wsc.originAddr +func (wsc *WebsocketService) URL() *url.URL { + return wsc.originURL } func (wsc *WebsocketService) Summary() string { - return fmt.Sprintf("Websocket listening on %ss", wsc.originAddr) + return fmt.Sprintf("Websocket listening on %s", wsc.originURL) } func (wsc *WebsocketService) Shutdown() { @@ -141,10 +142,10 @@ func (wsc *WebsocketService) Shutdown() { // HelloWorldService talks to the hello world example origin type HelloWorldService struct { - client http.RoundTripper - listener net.Listener - originAddr string - shutdownC chan struct{} + client http.RoundTripper + listener net.Listener + originURL *url.URL + shutdownC chan struct{} } func NewHelloWorldService(transport http.RoundTripper) (OriginService, error) { @@ -157,17 +158,19 @@ func NewHelloWorldService(transport http.RoundTripper) (OriginService, error) { hello.StartHelloWorldServer(log.CreateLogger(), listener, shutdownC) }() return &HelloWorldService{ - client: transport, - listener: listener, - originAddr: listener.Addr().String(), - shutdownC: shutdownC, + client: transport, + listener: listener, + originURL: &url.URL{ + Scheme: "https", + Host: listener.Addr().String(), + }, + shutdownC: shutdownC, }, 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") @@ -186,12 +189,12 @@ func (hwc *HelloWorldService) Proxy(stream *h2mux.MuxedStream, req *http.Request return resp, nil } -func (hwc *HelloWorldService) OriginAddr() string { - return hwc.originAddr +func (hwc *HelloWorldService) URL() *url.URL { + return hwc.originURL } func (hwc *HelloWorldService) Summary() string { - return fmt.Sprintf("Hello World service listening on %s", hwc.originAddr) + return fmt.Sprintf("Hello World service listening on %s", hwc.originURL) } func (hwc *HelloWorldService) Shutdown() { diff --git a/streamhandler/request.go b/streamhandler/request.go index 3b61ac26..40791d06 100644 --- a/streamhandler/request.go +++ b/streamhandler/request.go @@ -23,8 +23,8 @@ func IsLBProbeRequest(req *http.Request) bool { return strings.HasPrefix(req.UserAgent(), lbProbeUserAgentPrefix) } -func createRequest(stream *h2mux.MuxedStream, url string) (*http.Request, error) { - req, err := http.NewRequest(http.MethodGet, url, h2mux.MuxedStreamReader{MuxedStream: stream}) +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") } diff --git a/streamhandler/stream_handler.go b/streamhandler/stream_handler.go index 73d5d474..83d65d90 100644 --- a/streamhandler/stream_handler.go +++ b/streamhandler/stream_handler.go @@ -138,7 +138,7 @@ func (s *StreamHandler) serveRequest(stream *h2mux.MuxedStream) error { return fmt.Errorf("cannot map tunnel hostname %s to origin", tunnelHostname) } - req, err := createRequest(stream, originService.OriginAddr()) + req, err := createRequest(stream, originService.URL()) if err != nil { s.writeErrorStatus(stream, statusBadRequest) return errors.Wrap(err, "cannot create request") diff --git a/streamhandler/stream_handler_test.go b/streamhandler/stream_handler_test.go index 7e777709..c2d95b69 100644 --- a/streamhandler/stream_handler_test.go +++ b/streamhandler/stream_handler_test.go @@ -6,7 +6,6 @@ import ( "net" "net/http" "net/http/httptest" - "net/url" "strconv" "sync" "testing" @@ -46,16 +45,12 @@ func TestServeRequest(t *testing.T) { message := []byte("Hello cloudflared") httpServer := httptest.NewServer(&mockHTTPHandler{message}) - url, err := url.Parse(httpServer.URL) - assert.NoError(t, err) reverseProxyConfigs := []*pogs.ReverseProxyConfig{ { TunnelHostname: testTunnelHostname, Origin: &pogs.HTTPOriginConfig{ - URL: &pogs.HTTPURL{ - URL: url, - }, + URLString: httpServer.URL, }, }, } @@ -103,9 +98,7 @@ func TestServeBadRequest(t *testing.T) { { TunnelHostname: testTunnelHostname, Origin: &pogs.HTTPOriginConfig{ - URL: &pogs.HTTPURL{ - URL: &url.URL{}, - }, + URLString: "", }, }, } diff --git a/tunnelhostnamemapper/tunnelhostnamemapper_test.go b/tunnelhostnamemapper/tunnelhostnamemapper_test.go index 4c7fd0d8..e38d0611 100644 --- a/tunnelhostnamemapper/tunnelhostnamemapper_test.go +++ b/tunnelhostnamemapper/tunnelhostnamemapper_test.go @@ -3,6 +3,7 @@ package tunnelhostnamemapper import ( "fmt" "net/http" + "net/url" "sync" "testing" @@ -25,7 +26,9 @@ func TestTunnelHostnameMapperConcurrentAccess(t *testing.T) { assert.Nil(t, os) }) - httpOS := originservice.NewHTTPService(http.DefaultTransport, "127.0.0.1:8080", false) + 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) }) @@ -36,7 +39,9 @@ func TestTunnelHostnameMapperConcurrentAccess(t *testing.T) { assert.Equal(t, httpOS, os) }) - secondHTTPOS := originservice.NewHTTPService(http.DefaultTransport, "127.0.0.1:8090", true) + 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) diff --git a/tunnelrpc/pogs/config.go b/tunnelrpc/pogs/config.go index 570a8ed2..67926a0c 100644 --- a/tunnelrpc/pogs/config.go +++ b/tunnelrpc/pogs/config.go @@ -134,49 +134,18 @@ type OriginConfig interface { } type HTTPOriginConfig struct { - URL OriginAddr `capnp:"url"` - 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 - ProxyConnectTimeout time.Duration - ExpectContinueTimeout time.Duration - ChunkedEncoding bool -} - -type OriginAddr interface { - Addr() string -} - -type HTTPURL struct { - URL *url.URL -} - -func (ha *HTTPURL) Addr() string { - return ha.URL.String() -} - -func (ha *HTTPURL) capnpHTTPURL() *CapnpHTTPURL { - return &CapnpHTTPURL{ - URL: ha.URL.String(), - } -} - -// URL for a HTTP origin, capnp doesn't have native support for URL, so represent it as string -type CapnpHTTPURL struct { - URL string `capnp:"url"` -} - -type UnixPath struct { - Path string -} - -func (up *UnixPath) Addr() string { - return up.Path + 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() (originservice.OriginService, error) { @@ -184,8 +153,9 @@ func (hc *HTTPOriginConfig) Service() (originservice.OriginService, error) { if err != nil { return nil, err } + dialContext := (&net.Dialer{ - Timeout: hc.ProxyConnectTimeout, + Timeout: hc.ProxyConnectionTimeout, KeepAlive: hc.TCPKeepAlive, DualStack: hc.DialDualStack, }).DialContext @@ -202,18 +172,22 @@ func (hc *HTTPOriginConfig) Service() (originservice.OriginService, error) { IdleConnTimeout: hc.IdleConnectionTimeout, ExpectContinueTimeout: hc.ExpectContinueTimeout, } - if unixPath, ok := hc.URL.(*UnixPath); ok { + 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", unixPath.Addr()) + return dialContext(ctx, "unix", url.Host) } } - return originservice.NewHTTPService(transport, hc.URL.Addr(), hc.ChunkedEncoding), nil + return originservice.NewHTTPService(transport, url, hc.ChunkedEncoding), nil } func (_ *HTTPOriginConfig) isOriginConfig() {} type WebSocketOriginConfig struct { - URL string `capnp:"url"` + URLString string `capnp:"urlString"` TLSVerify bool `capnp:"tlsVerify"` OriginCAPool string OriginServerName string @@ -229,7 +203,12 @@ func (wsc *WebSocketOriginConfig) Service() (originservice.OriginService, error) ServerName: wsc.OriginServerName, InsecureSkipVerify: wsc.TLSVerify, } - return originservice.NewWebSocketService(tlsConfig, wsc.URL) + + 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) } func (_ *WebSocketOriginConfig) isOriginConfig() {} @@ -550,115 +529,12 @@ func UnmarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig) (*ReverseProxyC } func MarshalHTTPOriginConfig(s tunnelrpc.HTTPOriginConfig, p *HTTPOriginConfig) error { - switch originAddr := p.URL.(type) { - case *HTTPURL: - ss, err := s.OriginAddr().NewHttp() - if err != nil { - return err - } - if err := MarshalHTTPURL(ss, originAddr); err != nil { - return err - } - case *UnixPath: - ss, err := s.OriginAddr().NewUnix() - if err != nil { - return err - } - if err := MarshalUnixPath(ss, originAddr); err != nil { - return err - } - default: - return fmt.Errorf("Unknown type for OriginAddr: %T", originAddr) - } - s.SetTcpKeepAlive(p.TCPKeepAlive.Nanoseconds()) - s.SetDialDualStack(p.DialDualStack) - s.SetTlsHandshakeTimeout(p.TLSHandshakeTimeout.Nanoseconds()) - s.SetTlsVerify(p.TLSVerify) - s.SetOriginCAPool(p.OriginCAPool) - s.SetOriginServerName(p.OriginServerName) - s.SetMaxIdleConnections(p.MaxIdleConnections) - s.SetIdleConnectionTimeout(p.IdleConnectionTimeout.Nanoseconds()) - s.SetProxyConnectionTimeout(p.ProxyConnectTimeout.Nanoseconds()) - s.SetExpectContinueTimeout(p.ExpectContinueTimeout.Nanoseconds()) - s.SetChunkedEncoding(p.ChunkedEncoding) - return nil + return pogs.Insert(tunnelrpc.HTTPOriginConfig_TypeID, s.Struct, p) } func UnmarshalHTTPOriginConfig(s tunnelrpc.HTTPOriginConfig) (*HTTPOriginConfig, error) { p := new(HTTPOriginConfig) - switch s.OriginAddr().Which() { - case tunnelrpc.HTTPOriginConfig_originAddr_Which_http: - ss, err := s.OriginAddr().Http() - if err != nil { - return nil, err - } - originAddr, err := UnmarshalCapnpHTTPURL(ss) - if err != nil { - return nil, err - } - p.URL = originAddr - case tunnelrpc.HTTPOriginConfig_originAddr_Which_unix: - ss, err := s.OriginAddr().Unix() - if err != nil { - return nil, err - } - originAddr, err := UnmarshalUnixPath(ss) - if err != nil { - return nil, err - } - p.URL = originAddr - default: - return nil, fmt.Errorf("Unknown type for OriginAddr: %T", s.OriginAddr().Which()) - } - p.TCPKeepAlive = time.Duration(s.TcpKeepAlive()) - p.DialDualStack = s.DialDualStack() - p.TLSHandshakeTimeout = time.Duration(s.TlsHandshakeTimeout()) - p.TLSVerify = s.TlsVerify() - originCAPool, err := s.OriginCAPool() - if err != nil { - return nil, err - } - p.OriginCAPool = originCAPool - originServerName, err := s.OriginServerName() - if err != nil { - return nil, err - } - p.OriginServerName = originServerName - p.MaxIdleConnections = s.MaxIdleConnections() - p.IdleConnectionTimeout = time.Duration(s.IdleConnectionTimeout()) - p.ProxyConnectTimeout = time.Duration(s.ProxyConnectionTimeout()) - p.ExpectContinueTimeout = time.Duration(s.ExpectContinueTimeout()) - p.ChunkedEncoding = s.ChunkedEncoding() - return p, nil -} - -func MarshalHTTPURL(s tunnelrpc.CapnpHTTPURL, p *HTTPURL) error { - return pogs.Insert(tunnelrpc.CapnpHTTPURL_TypeID, s.Struct, p.capnpHTTPURL()) -} - -func UnmarshalCapnpHTTPURL(s tunnelrpc.CapnpHTTPURL) (*HTTPURL, error) { - p := new(CapnpHTTPURL) - err := pogs.Extract(p, tunnelrpc.CapnpHTTPURL_TypeID, s.Struct) - if err != nil { - return nil, err - } - url, err := url.Parse(p.URL) - if err != nil { - return nil, err - } - return &HTTPURL{ - URL: url, - }, nil -} - -func MarshalUnixPath(s tunnelrpc.UnixPath, p *UnixPath) error { - err := pogs.Insert(tunnelrpc.UnixPath_TypeID, s.Struct, p) - return err -} - -func UnmarshalUnixPath(s tunnelrpc.UnixPath) (*UnixPath, error) { - p := new(UnixPath) - err := pogs.Extract(p, tunnelrpc.UnixPath_TypeID, s.Struct) + err := pogs.Extract(p, tunnelrpc.HTTPOriginConfig_TypeID, s.Struct) return p, err } diff --git a/tunnelrpc/pogs/config_test.go b/tunnelrpc/pogs/config_test.go index 16f41a04..bb1299a5 100644 --- a/tunnelrpc/pogs/config_test.go +++ b/tunnelrpc/pogs/config_test.go @@ -2,7 +2,6 @@ package pogs import ( "fmt" - "net/url" "reflect" "testing" "time" @@ -205,6 +204,24 @@ func TestWebSocketOriginConfig(t *testing.T) { } } +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", + }, + } + + for _, config := range invalidConfigs { + service, err := config.Service() + assert.Error(t, err) + assert.Nil(t, service) + } +} + ////////////////////////////////////////////////////////////////////////////// // Functions to generate sample data for ease of testing @@ -260,23 +277,18 @@ func sampleReverseProxyConfig(overrides ...func(*ReverseProxyConfig)) *ReversePr func sampleHTTPOriginConfig(overrides ...func(*HTTPOriginConfig)) *HTTPOriginConfig { sample := &HTTPOriginConfig{ - URL: &HTTPURL{ - URL: &url.URL{ - Scheme: "https", - Host: "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, - ProxyConnectTimeout: 15 * time.Second, - ExpectContinueTimeout: 21 * time.Second, - ChunkedEncoding: true, + 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 { @@ -287,20 +299,18 @@ func sampleHTTPOriginConfig(overrides ...func(*HTTPOriginConfig)) *HTTPOriginCon func sampleHTTPOriginUnixPathConfig(overrides ...func(*HTTPOriginConfig)) *HTTPOriginConfig { sample := &HTTPOriginConfig{ - URL: &UnixPath{ - Path: "/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, - ProxyConnectTimeout: 15 * time.Second, - ExpectContinueTimeout: 21 * time.Second, - ChunkedEncoding: true, + 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 { @@ -311,7 +321,7 @@ func sampleHTTPOriginUnixPathConfig(overrides ...func(*HTTPOriginConfig)) *HTTPO func sampleWebSocketOriginConfig(overrides ...func(*WebSocketOriginConfig)) *WebSocketOriginConfig { sample := &WebSocketOriginConfig{ - URL: "ssh://example.com", + URLString: "ssh://example.com", TLSVerify: true, OriginCAPool: "/etc/cert.pem", OriginServerName: "secure.example.com", diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp index 1ce9218b..5d178278 100644 --- a/tunnelrpc/tunnelrpc.capnp +++ b/tunnelrpc/tunnelrpc.capnp @@ -147,7 +147,7 @@ struct WebSocketOriginConfig { # cloudflared will start a websocket server that forwards data to this URI # cloudflared CLI option: `url` # cloudflared logic: https://github.com/cloudflare/cloudflared/blob/2019.3.2/cmd/cloudflared/tunnel/cmd.go#L304 - url @0 :Text; + urlString @0 :Text; # Whether cloudflared should verify TLS connections to the origin. # negation of cloudflared CLI option: `no-tls-verify` tlsVerify @1 :Bool; @@ -168,25 +168,22 @@ struct WebSocketOriginConfig { struct HTTPOriginConfig { # HTTP(S) URL of the origin service. # cloudflared CLI option: `url` - originAddr :union { - http @0 :CapnpHTTPURL; - unix @1 :UnixPath; - } + urlString @0 :Text; # the TCP keep-alive period (in ns) for an active network connection. # Zero means keep-alives are not enabled. # cloudflared CLI option: `proxy-tcp-keepalive` - tcpKeepAlive @2 :Int64; + tcpKeepAlive @1 :Int64; # whether cloudflared should use a "happy eyeballs"-compliant procedure # to connect to origins that resolve to both IPv4 and IPv6 addresses # negation of cloudflared CLI option: `proxy-no-happy-eyeballs` - dialDualStack @3 :Bool; + dialDualStack @2 :Bool; # maximum time (in ns) for cloudflared to wait for a TLS handshake # with the origin. Zero means no timeout. # cloudflared CLI option: `proxy-tls-timeout` - tlsHandshakeTimeout @4 :Int64; + tlsHandshakeTimeout @3 :Int64; # Whether cloudflared should verify TLS connections to the origin. # negation of cloudflared CLI option: `no-tls-verify` - tlsVerify @5 :Bool; + tlsVerify @4 :Bool; # originCAPool specifies the root CA that cloudflared should use when # verifying TLS connections to the origin. # - if tlsVerify is false, originCAPool will be ignored. @@ -195,39 +192,29 @@ struct HTTPOriginConfig { # - if tlsVerify is true and originCAPool is non-empty, cloudflared will # treat it as the filepath to the root CA. # cloudflared CLI option: `origin-ca-pool` - originCAPool @6 :Text; + originCAPool @5 :Text; # Hostname to use when verifying TLS connections to the origin. # cloudflared CLI option: `origin-server-name` - originServerName @7 :Text; + originServerName @6 :Text; # maximum number of idle (keep-alive) connections for cloudflared to # keep open with the origin. Zero means no limit. # cloudflared CLI option: `proxy-keepalive-connections` - maxIdleConnections @8 :UInt64; + maxIdleConnections @7 :UInt64; # maximum time (in ns) for an idle (keep-alive) connection to remain # idle before closing itself. Zero means no timeout. # cloudflared CLI option: `proxy-keepalive-timeout` - idleConnectionTimeout @9 :Int64; + idleConnectionTimeout @8 :Int64; # maximum amount of time a dial will wait for a connect to complete. - proxyConnectionTimeout @10 :Int64; + proxyConnectionTimeout @9 :Int64; # The amount of time to wait for origin's first response headers after fully # writing the request headers if the request has an "Expect: 100-continue" header. # Zero means no timeout and causes the body to be sent immediately, without # waiting for the server to approve. - expectContinueTimeout @11 :Int64; + expectContinueTimeout @10 :Int64; # Whether cloudflared should allow chunked transfer encoding to the # origin. (This should be disabled for WSGI origins, for example.) # negation of cloudflared CLI option: `no-chunked-encoding` - chunkedEncoding @12 :Bool; -} - -# URL for a HTTP origin, capnp doesn't have native support for URL, so represent it as Text -struct CapnpHTTPURL { - url @0: Text; -} - -# Path to a unix socket -struct UnixPath { - path @0: Text; + chunkedEncoding @11 :Bool; } # configuration for cloudflared to provide a DNS over HTTPS proxy server diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go index fdd276ed..14b38c8b 100644 --- a/tunnelrpc/tunnelrpc.capnp.go +++ b/tunnelrpc/tunnelrpc.capnp.go @@ -1451,22 +1451,22 @@ func (s WebSocketOriginConfig) String() string { return str } -func (s WebSocketOriginConfig) Url() (string, error) { +func (s WebSocketOriginConfig) UrlString() (string, error) { p, err := s.Struct.Ptr(0) return p.Text(), err } -func (s WebSocketOriginConfig) HasUrl() bool { +func (s WebSocketOriginConfig) HasUrlString() bool { p, err := s.Struct.Ptr(0) return p.IsValid() || err != nil } -func (s WebSocketOriginConfig) UrlBytes() ([]byte, error) { +func (s WebSocketOriginConfig) UrlStringBytes() ([]byte, error) { p, err := s.Struct.Ptr(0) return p.TextBytes(), err } -func (s WebSocketOriginConfig) SetUrl(v string) error { +func (s WebSocketOriginConfig) SetUrlString(v string) error { return s.Struct.SetText(0, v) } @@ -1547,25 +1547,6 @@ func (p WebSocketOriginConfig_Promise) Struct() (WebSocketOriginConfig, error) { } type HTTPOriginConfig struct{ capnp.Struct } -type HTTPOriginConfig_originAddr HTTPOriginConfig -type HTTPOriginConfig_originAddr_Which uint16 - -const ( - HTTPOriginConfig_originAddr_Which_http HTTPOriginConfig_originAddr_Which = 0 - HTTPOriginConfig_originAddr_Which_unix HTTPOriginConfig_originAddr_Which = 1 -) - -func (w HTTPOriginConfig_originAddr_Which) String() string { - const s = "httpunix" - switch w { - case HTTPOriginConfig_originAddr_Which_http: - return s[0:4] - case HTTPOriginConfig_originAddr_Which_unix: - return s[4:8] - - } - return "HTTPOriginConfig_originAddr_Which(" + strconv.FormatUint(uint64(w), 10) + ")" -} // HTTPOriginConfig_TypeID is the unique identifier for the type HTTPOriginConfig. const HTTPOriginConfig_TypeID = 0xe4a6a1bc139211b4 @@ -1590,93 +1571,39 @@ func (s HTTPOriginConfig) String() string { return str } -func (s HTTPOriginConfig) OriginAddr() HTTPOriginConfig_originAddr { - return HTTPOriginConfig_originAddr(s) -} - -func (s HTTPOriginConfig_originAddr) Which() HTTPOriginConfig_originAddr_Which { - return HTTPOriginConfig_originAddr_Which(s.Struct.Uint16(0)) -} -func (s HTTPOriginConfig_originAddr) Http() (CapnpHTTPURL, error) { - if s.Struct.Uint16(0) != 0 { - panic("Which() != http") - } +func (s HTTPOriginConfig) UrlString() (string, error) { p, err := s.Struct.Ptr(0) - return CapnpHTTPURL{Struct: p.Struct()}, err + return p.Text(), err } -func (s HTTPOriginConfig_originAddr) HasHttp() bool { - if s.Struct.Uint16(0) != 0 { - return false - } +func (s HTTPOriginConfig) HasUrlString() bool { p, err := s.Struct.Ptr(0) return p.IsValid() || err != nil } -func (s HTTPOriginConfig_originAddr) SetHttp(v CapnpHTTPURL) error { - s.Struct.SetUint16(0, 0) - return s.Struct.SetPtr(0, v.Struct.ToPtr()) -} - -// NewHttp sets the http field to a newly -// allocated CapnpHTTPURL struct, preferring placement in s's segment. -func (s HTTPOriginConfig_originAddr) NewHttp() (CapnpHTTPURL, error) { - s.Struct.SetUint16(0, 0) - ss, err := NewCapnpHTTPURL(s.Struct.Segment()) - if err != nil { - return CapnpHTTPURL{}, err - } - err = s.Struct.SetPtr(0, ss.Struct.ToPtr()) - return ss, err -} - -func (s HTTPOriginConfig_originAddr) Unix() (UnixPath, error) { - if s.Struct.Uint16(0) != 1 { - panic("Which() != unix") - } +func (s HTTPOriginConfig) UrlStringBytes() ([]byte, error) { p, err := s.Struct.Ptr(0) - return UnixPath{Struct: p.Struct()}, err + return p.TextBytes(), err } -func (s HTTPOriginConfig_originAddr) HasUnix() bool { - if s.Struct.Uint16(0) != 1 { - return false - } - p, err := s.Struct.Ptr(0) - return p.IsValid() || err != nil -} - -func (s HTTPOriginConfig_originAddr) SetUnix(v UnixPath) error { - s.Struct.SetUint16(0, 1) - return s.Struct.SetPtr(0, v.Struct.ToPtr()) -} - -// NewUnix sets the unix field to a newly -// allocated UnixPath struct, preferring placement in s's segment. -func (s HTTPOriginConfig_originAddr) NewUnix() (UnixPath, error) { - s.Struct.SetUint16(0, 1) - ss, err := NewUnixPath(s.Struct.Segment()) - if err != nil { - return UnixPath{}, err - } - err = s.Struct.SetPtr(0, ss.Struct.ToPtr()) - return ss, err +func (s HTTPOriginConfig) SetUrlString(v string) error { + return s.Struct.SetText(0, v) } func (s HTTPOriginConfig) TcpKeepAlive() int64 { - return int64(s.Struct.Uint64(8)) + return int64(s.Struct.Uint64(0)) } func (s HTTPOriginConfig) SetTcpKeepAlive(v int64) { - s.Struct.SetUint64(8, uint64(v)) + s.Struct.SetUint64(0, uint64(v)) } func (s HTTPOriginConfig) DialDualStack() bool { - return s.Struct.Bit(16) + return s.Struct.Bit(64) } func (s HTTPOriginConfig) SetDialDualStack(v bool) { - s.Struct.SetBit(16, v) + s.Struct.SetBit(64, v) } func (s HTTPOriginConfig) TlsHandshakeTimeout() int64 { @@ -1688,11 +1615,11 @@ func (s HTTPOriginConfig) SetTlsHandshakeTimeout(v int64) { } func (s HTTPOriginConfig) TlsVerify() bool { - return s.Struct.Bit(17) + return s.Struct.Bit(65) } func (s HTTPOriginConfig) SetTlsVerify(v bool) { - s.Struct.SetBit(17, v) + s.Struct.SetBit(65, v) } func (s HTTPOriginConfig) OriginCAPool() (string, error) { @@ -1766,11 +1693,11 @@ func (s HTTPOriginConfig) SetExpectContinueTimeout(v int64) { } func (s HTTPOriginConfig) ChunkedEncoding() bool { - return s.Struct.Bit(18) + return s.Struct.Bit(66) } func (s HTTPOriginConfig) SetChunkedEncoding(v bool) { - s.Struct.SetBit(18, v) + s.Struct.SetBit(66, v) } // HTTPOriginConfig_List is a list of HTTPOriginConfig. @@ -1801,166 +1728,6 @@ func (p HTTPOriginConfig_Promise) Struct() (HTTPOriginConfig, error) { return HTTPOriginConfig{s}, err } -func (p HTTPOriginConfig_Promise) OriginAddr() HTTPOriginConfig_originAddr_Promise { - return HTTPOriginConfig_originAddr_Promise{p.Pipeline} -} - -// HTTPOriginConfig_originAddr_Promise is a wrapper for a HTTPOriginConfig_originAddr promised by a client call. -type HTTPOriginConfig_originAddr_Promise struct{ *capnp.Pipeline } - -func (p HTTPOriginConfig_originAddr_Promise) Struct() (HTTPOriginConfig_originAddr, error) { - s, err := p.Pipeline.Struct() - return HTTPOriginConfig_originAddr{s}, err -} - -func (p HTTPOriginConfig_originAddr_Promise) Http() CapnpHTTPURL_Promise { - return CapnpHTTPURL_Promise{Pipeline: p.Pipeline.GetPipeline(0)} -} - -func (p HTTPOriginConfig_originAddr_Promise) Unix() UnixPath_Promise { - return UnixPath_Promise{Pipeline: p.Pipeline.GetPipeline(0)} -} - -type CapnpHTTPURL struct{ capnp.Struct } - -// CapnpHTTPURL_TypeID is the unique identifier for the type CapnpHTTPURL. -const CapnpHTTPURL_TypeID = 0xa160eb416f17c28e - -func NewCapnpHTTPURL(s *capnp.Segment) (CapnpHTTPURL, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) - return CapnpHTTPURL{st}, err -} - -func NewRootCapnpHTTPURL(s *capnp.Segment) (CapnpHTTPURL, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) - return CapnpHTTPURL{st}, err -} - -func ReadRootCapnpHTTPURL(msg *capnp.Message) (CapnpHTTPURL, error) { - root, err := msg.RootPtr() - return CapnpHTTPURL{root.Struct()}, err -} - -func (s CapnpHTTPURL) String() string { - str, _ := text.Marshal(0xa160eb416f17c28e, s.Struct) - return str -} - -func (s CapnpHTTPURL) Url() (string, error) { - p, err := s.Struct.Ptr(0) - return p.Text(), err -} - -func (s CapnpHTTPURL) HasUrl() bool { - p, err := s.Struct.Ptr(0) - return p.IsValid() || err != nil -} - -func (s CapnpHTTPURL) UrlBytes() ([]byte, error) { - p, err := s.Struct.Ptr(0) - return p.TextBytes(), err -} - -func (s CapnpHTTPURL) SetUrl(v string) error { - return s.Struct.SetText(0, v) -} - -// CapnpHTTPURL_List is a list of CapnpHTTPURL. -type CapnpHTTPURL_List struct{ capnp.List } - -// NewCapnpHTTPURL creates a new list of CapnpHTTPURL. -func NewCapnpHTTPURL_List(s *capnp.Segment, sz int32) (CapnpHTTPURL_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz) - return CapnpHTTPURL_List{l}, err -} - -func (s CapnpHTTPURL_List) At(i int) CapnpHTTPURL { return CapnpHTTPURL{s.List.Struct(i)} } - -func (s CapnpHTTPURL_List) Set(i int, v CapnpHTTPURL) error { return s.List.SetStruct(i, v.Struct) } - -func (s CapnpHTTPURL_List) String() string { - str, _ := text.MarshalList(0xa160eb416f17c28e, s.List) - return str -} - -// CapnpHTTPURL_Promise is a wrapper for a CapnpHTTPURL promised by a client call. -type CapnpHTTPURL_Promise struct{ *capnp.Pipeline } - -func (p CapnpHTTPURL_Promise) Struct() (CapnpHTTPURL, error) { - s, err := p.Pipeline.Struct() - return CapnpHTTPURL{s}, err -} - -type UnixPath struct{ capnp.Struct } - -// UnixPath_TypeID is the unique identifier for the type UnixPath. -const UnixPath_TypeID = 0xf7e406af6bd5236c - -func NewUnixPath(s *capnp.Segment) (UnixPath, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) - return UnixPath{st}, err -} - -func NewRootUnixPath(s *capnp.Segment) (UnixPath, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) - return UnixPath{st}, err -} - -func ReadRootUnixPath(msg *capnp.Message) (UnixPath, error) { - root, err := msg.RootPtr() - return UnixPath{root.Struct()}, err -} - -func (s UnixPath) String() string { - str, _ := text.Marshal(0xf7e406af6bd5236c, s.Struct) - return str -} - -func (s UnixPath) Path() (string, error) { - p, err := s.Struct.Ptr(0) - return p.Text(), err -} - -func (s UnixPath) HasPath() bool { - p, err := s.Struct.Ptr(0) - return p.IsValid() || err != nil -} - -func (s UnixPath) PathBytes() ([]byte, error) { - p, err := s.Struct.Ptr(0) - return p.TextBytes(), err -} - -func (s UnixPath) SetPath(v string) error { - return s.Struct.SetText(0, v) -} - -// UnixPath_List is a list of UnixPath. -type UnixPath_List struct{ capnp.List } - -// NewUnixPath creates a new list of UnixPath. -func NewUnixPath_List(s *capnp.Segment, sz int32) (UnixPath_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz) - return UnixPath_List{l}, err -} - -func (s UnixPath_List) At(i int) UnixPath { return UnixPath{s.List.Struct(i)} } - -func (s UnixPath_List) Set(i int, v UnixPath) error { return s.List.SetStruct(i, v.Struct) } - -func (s UnixPath_List) String() string { - str, _ := text.MarshalList(0xf7e406af6bd5236c, s.List) - return str -} - -// UnixPath_Promise is a wrapper for a UnixPath promised by a client call. -type UnixPath_Promise struct{ *capnp.Pipeline } - -func (p UnixPath_Promise) Struct() (UnixPath, error) { - s, err := p.Pipeline.Struct() - return UnixPath{s}, err -} - type DoHProxyConfig struct{ capnp.Struct } // DoHProxyConfig_TypeID is the unique identifier for the type DoHProxyConfig. @@ -3742,229 +3509,218 @@ func (p ClientService_useConfiguration_Results_Promise) Result() UseConfiguratio return UseConfigurationResult_Promise{Pipeline: p.Pipeline.GetPipeline(0)} } -const schema_db8274f9144abc7e = "x\xda\xacY{p\\\xe5u?\xe7\xde]]\xc9\x96" + - "\xbc{\xb9K\x1d\xc9\xd6l\xeb\x92I0\x98\xe2(\xb4" + - "\xa06Y\xadd9Z\xc7\x8f\xbdz\x180f\xc6\xd7" + - "\xbb\x9f\xa4k\xef\xde\xbb\xbe\x0f[rMl\\(\xa0" + - "\x1a0\x04\xcd\x80CR\xdb\xad\x0b\xa1P0!\xd3\x09" + - "%\x994}\x904\xd3!\x99\x86Ni\x93?\x1a\xf0" + - "t\x86\x96\xa1&x\x18:\x98\xdb9\xdf}j\xb5\xc8" + - "v\xa7\xfe\xc3\xda9\xfb=\xce\xf7;\xe7\xfc\xceco" + - "\xccv\x0c\x08\xeb\xd3\xafv\x01\xa8'\xd3m\xde\xef\xd5" + - "_;\xfd\xdb\xf3?\xbe\x07\xe4\x1e\xc1\xfb\xca+\x9br" + - "\x1f:G\xff\x0d\x00\xfb~\xd4v\x10\x95_\xb4I\x00" + - "\xca\x1bm\xdb\x00\xbd\x7f\xba\xf1\xd0[\xbb~\xf5\xc8\xfd" + - " \xf7`\xbc2%\x01\xf4\x9do\x9bC\xa5C\x92@" + - "\xf4\xbe~G\xee\x1f\xf0\xe4\x07\x8f\x80\xfcY\x04H#" + - "}}\xaem\x99\x00\xa8\\l+\x00z\xaf]\xff\xca" + - "\xcb\xc7\xbfu\xdf\xd7@\xfd\x0c\"\xf8\xfb{\xa5\xffA" + - "@e\xbdD\x0b\xce\xff\xe9u\xa9\xe7^\xbb\xea\x1b|" + - "\x81w\xe6\xa7\xb7\xbex\xfc[\xbf\xfe6L\x08\x12\xa6" + - "\x00\xfan\x97,Z\xcb\xa4\xff\x00\xf4\x1e\xfa\xc1J\xb3" + - "\xf8\x9f\xbbN-\xd4\xc9\xbfu\xb8\xbd\x1f\x95\x89vz" + - "\x80\xdaN\x07?\xf6/\xdf\xddZ\x7f\xe4\xc4i\x90?" + - "\x13^\xbc\xaf]\x10 \xe5\xdd\xf4\xaf\xe7\xb6myq" + - "\xf2)\xff\x1b\x7f;k\x7f\x91\xeeq\xf9\xd6\x1f\x1e\xc8" + - "\x1e+\xfe\xce\xc3O\x81\xda\x83\xc9\x8b\xf8!O\xb4\xcf" + - "\xa1r\x96.\xea{\xae=\x8f\x80\xde\xdc-\xdf\xdd\xfe" + - "\xab?\xb4\x9f\x01u\x1d\xa6\xbc\xbf}\xe0\xcd\xfd\xd7~" + - "s\xf2U\xfe\x04\x91\xf0\xe88MG_\xe8x\x1e\xd0" + - "\xeb\xfa\xab\xb5[\x1f~k\xf3Y:Zh~\xc3\xfc" + - "\xb2~T\xce,\xa37\x9cZF\xab\x7fr\xfd\xf6\xef" + - "}\xef\x85\xa9\xb3\xcd\x8a\x08\xb4\xba\xb8|\x13*\x13\xcb" + - "\xf9\x8b\x97\xd3\xea\xabK\xf8\xf3\xef\xafO\xfde\xf0." + - "\x91\x16\xa5;\xdf\xa6\xcb\xbb;i\xc1\x1d\x1f}\xfb\x07" + - "\xc3\xef\xfe\xec;Ik}\xa7S k\xfdc'=" + - "\xbc\xf7\x9d\xc1.\xe3\xdd\xa3\xdfo\x02\x98\x9ft\xa1s" + - "\x13*\x1d]t]\xba\xeby\xc0\x0f\x9e\xb9\xefx\xe9" + - "\xcd\x0d\xaf\xaa=\x98j~\xc8\xa9\xae\x83\xa8|\x9b\xd6" + - "\xf6\x9d\xed\xe2\x18E\xa84-\xe7/\xf9\xf7\x15{P" + - "\xb9\xb0\x82\xfb\xd6\x0a\xbe|\xd3\x1d_}4}\xee\xab" + - "\xaf6\xc3$\xd1\x9a\x0f3\x16*]Y\xfa\xd8\x91}" + - "J\x00\xf4z^\xf8\xdd\xbf\x18\xac\xbe\xf1\xe3&\xbd\xe9" + - "p\xe5\xc2U\xef)\xa8\xd0\xa7\x8bW\x1d\x00\xf4\xee\xbb" + - "n\xf6\xe0\xd6O\xcf\xbd\xde\x8c)W\xfcve\x0e\x95" + - "}|u]\xa1\xd5\xc29\xad\xfb\xc8?\x7f\xf1\xe7\x09" + - "/\xfa\x85\xf2K\x84\x94\xb7u\xfb\x1d{:\xeez\xf3" + - "\xcd\xa4\x17\xfdT\xe1h\x9fS\x08\xcc\x97\xe4G\x95W" + - "N\xfd\xd9[t\x91\xd4\x8cf:\xb7\x03\x95\xee\x1c}" + - "\xbc:\xc7\xdf\x10\xb9~+[_\xf8\xb5~T\xd2+" + - "I/\\Iz\xdd\xb4\xab\xc8v\xde|\xdb\xdb \xf7" + - "\x88\x0b\x02\xb9\xb8\xb2\x1f\x15\x95V\xf6mY)\xa1r" + - "\x91>z\x0fM\xed\xf8\xd1\xf9\xa1S\xff\xdd\xd2\xa3\xcf" + - "\xd1\x96\x0b|\xcb\xf9\x95\x1c\xfe\xbe\xf5\x7f\xf4\xce\xfc\x9f" + - "\x0c\x9d_t\xfa\x17\xba\x07Q\xd9\xd2Mz\x94\xba\xbf" + - "\xa4\xccv\xf3\xc3\xbf\xb2a\xdb-k\xfe\xfa\xbd$\x12" + - "Z\xf7{<\x9e\xba\x09\x89\xc9\x9b\xff\xebK\x9f~\xe8" + - "\xef\xdfk\x15\xb7\xf3\xddkQ9\xc3Oe\x98\x16\xf3\xaa\xba]1\x0d" + - "\x83\x81Xq\x0e\xef\xd6j\x9aQa\xd1E\xe9\xc5\x17" + - "\x8d\xb0Z\xcd\xbc\xd5\xb4j\xd5m\x96>\xa5\x1bC\xa6" + - "1\xa9O\x01\x94\x11\xa3m\xd2\xe2mC5\x9d\x19\xce" + - "\x18\xb3\xf6\xeb\x15v\x83k3\x7f\x9fki\x8en\x1a" + - "\xd7\x8c2\xdb\xad96\x80\x9a\x12S\x00)\x04\x90\xbb" + - "\xfa\x01\xd4v\x11\xd5\x9c\x80\x05\x8b/\xc0l\x1c\xd2\x80" + - "\x98\x85\xf8\xce\xb6\xc5w\xfaX\xd0\x9d\xcc\xba\xc15," + - "6\xa5\xdb\x0e\xb3|\xf15\x85\xb2fiu;y\xe1" + - "\x09\x005+\xa2\xbaZ@o\xca\xd2*\xac\xcc,\xd4" + - "\xcd\xeaV\xcd0\xc7DV\xc14\x08\x98N\\\xda\xc2" + - "\x10\x1b5\xbd\xc6\xaa\xfe\xebn\xa8\xe4\xf9_5+\xa6" + - ":=\x8f_\xa2\xed\x00Pw\x89\xa8\xd6\x04\xec\xc2\x8f" + - "\xbd\x1c\xa5JY?\x08\xa0N\x8b\xa8:\x02v\x09\x17" + - "\xbd\x1c\xb7\xda\xbe5\x00jMDuF\xc0.\xf1#" + - "/G9Fv\xf7\x00\xa8\x8e\x88\xea\x11\x01=\xdbm" + - "\x10\xa66\x88\xa6\x85\xd9\xd8\xed\x03tXu\x8a\x906" + - "\xa0\xc0*\x044fC*\xf7\x17HUs\x1a\xb3q" + - "\xee\x09\xb6Yl?\xb3lV\x86\x8ce\xce\xccb6" + - "\xa6\xf4&\xd4\xc5\x16\x96\xa6\xffG\x0a\xe3\xe3\xe5\x89\xd1" + - "\xcd\xe4\x81\x09\x80\xd7\xc4\x16\x95\\\xab\x86\x9d `g" + - "\xe2\xb8\xae+5b\xe87\xd1\xae\xa5\xf7sO\xaf8" + - "\xd7\x94\xf3\x8blOf\xe9\x14Q\xfd\x94\x80^\x83\xbe" + - "e\x0e\x03\xd1\xb21\x1b\x97\x08M\x8fO\x7f\xc2\xe3\x87" + - "\xfc[\xca\xc1)\x96\xcd\xa3C\xcdE\x97\xddE\x97\x1d" + - "\x12Q\xbd_@\x19\xd1w\x81{-\x00\xf5\x1e\x11\xd5" + - "\xe3\x02\xa2\xe0;\xc0\x83\xa7\x01\xd4\xe3\"\xaaO\x0a(" + - "\x8b\x82o\xff'\xd6\x02\xa8\x8f\x89\xa8\xbe \xa0\x9c\x12" + - "sT;\xc9\xcf\x91\xef\xbe \xa2\xfa\x8a\x80\x9e\xe9G" + - "&\xe9\xef`\x17\x08\xd8\x05\xe8Uj\xa6[\x9d\xaci" + - "\x90\xb7X\xb5\xb4!\x92\x1bn\xbdl\xb1\xfd:\x9a\xae" + - "]t\x1cV\x97\x1a\x8e\x8dm `\x1b`\xc6\xd1\xa6" + - "l\\\x01X\x16\x11\xb3q\xee\x05$at&Z\xac" + - "\xba\x9dY\xb6.\x9a\xc6\"\xa3\xb6\x80i4\xf0/\xf2" + - "\xae TLK\x97\xa6tC\xed\x14S\xab=/\xc0" + - "d\x98\x9e: \xa2\xbaY\xc0^\xfc\x98\xc4\x04Ki" + - "\x14@\x1d\x11Q\x1d\x17\xb0W\xb8Hb\x02F%X" + - "\xcb\"\xaa;\x05\xccL;N\x03\xb31=\x07\xb6;" + - "\xc0v\xdbfe/\x03$2\x89\x88?\xf8v: " + - "7\x10kU\xcc\xc6u\xf1ex=\xb7y\xc1\x19\xb6" + - ",\xd3\xe2\xbc\x1bY{\xf8s\xf1#Bc\x97v\xc4" + - "/\x90\x85\x01\xffY\xea\xeeX\xff|Esm\x16a" + - "i1\xc7\x9a-N: 2+b!{\xdatk" + - "\xd5Q\x06\x92c\xcd\"\x82\x80\xb847m0G\x12" + - "\x90\xfb^\x99\xd0\x93t\xda \xa2Z\x8e\xf5\xdcB\xb2" + - "\xcd\"\xaa\xb7\x91\x9e\x01\xfc\x13\x04\xff\xb8\x88jC@" + - "\xafF\xe1h\x8c\x98 \xdaN\xa4\xae/,\x9b\xdc\x01" + - "%\x10P\x02\xf4\xdc\x86\xedXL\xab\x03F\x1eE\xeb" + - "W\\\x01\x897E\x7fY\xcb\xf00n\xfd\x86(\xb2" + - "\xb6lJ>\"\x08\xad\x89\xc1\x18\xec\xd6\x013m\xda" + - "\x8e\xa1\xd5\x19\x00\x84\x0f;l6\x88E\x89\x14\xa2\xaa" + - "\xb5\xc97\xae<\xf7\xf9yhA\xe6;\x9dHD\x95" + - "`7\xf2\xedC\xa6!M\xeaS\x98\x8d\xcb\xbc&\x05" + - "Z\xd8\xbd\xe8:\xd3\xccp\xf4\x0a\xbfp\x91\xdd\xd7\xc4" + - "\xfe\x19aV\xfa\\\x02\xc8\x10\xb3-\xbbc \xa5\xbd" + - "l6\x84%\xcf\xea\x9a\x1e\xb3y\x80f\x11\xa4/\xc7" + - "k\x96,^\x82,\xe5\xe7\xa8\x82\x0fO\x13e\xce\x01" + - "\xa8GDT\x8f%\x94|\xe0Q\x00\xf5\x98\x88\xea\xe3" + - "\x09%\xe7\x07\x93\x9c)\x06\x9cI\x88>)\xa2\xfa\xb4" + - "\x80\x98\xf2)\xf3\x0cQ\xe6\xd3\"\xaa/\x09\x9c\x05G" + - "\x8aC\xa6\x81\x81\x126@\xc8\x81\xde4\xd3,g7" + - "\xd3\xd0)\x19\x0e\xb3\xf6kX\x0bc\xf0\xb0\xa3\xd7\x99" + - "\xe9:QL\xd6\xb5\x19^\x01`u\xc4\xdf%i\x8e" + - "\x8d\x1d `\x07\x85\x80\xcd\xac!\x8bU\x91\xac\xa1\xd5" + - "\xca\x9a\xe8L_\x0e@\x0b\xf92\xd3\x02\x9e\x83qF" + - "\xa1\x7fq\x9f*\xdf\xdb\x0f\x02\x0f]zs}0\xae" + - "3xBIS\x99\xf1h\\P\xf0\x84\xd2F'\x9e" + - "\x88\x01\x0fT\x1b1\xa1\xe0\x87D\xa8s\xc17\xf5a" + - "\xa2'\x9d\xc5\xef\x0c\xf2\xac\x8e\xa61\xce\x01\xc2\x18\xa1" + - "\x8aYoX\xcc\xb6Q7\x0d\xd5\xd5j\xba\xe8\xccF" + - "\x1b\x97\xc4\x80b\xdf\x8f\x99m\x8d<7\x12\x81pc" + - "\x08\x82R\xc4M\x00c\x03(\xe2\xd8f\x8c\xddD)" + - "\xe1 \xc0\xd8\x06\x92\x971\xf6\x14e\x0b\xf6\x00\x8c\x8d" + - "\x90|\x1c\x05D\xdfW\x14\x15\x9f\x01\x18\x1b'\xf1." + - "\x8cS\xacr'?~'\xc9\xa7I\x9eNq\xf8\x14" + - "\x86k\x01\xc6v\x91\xfc\x10\xc9\xdb\x04\x8e\xa02\x8b{" + - "\x00\xc6fH~\x0f\xc9\xa5t\x8e\xea\x7f\xe5n\xb4\x00" + - "\xc6\x8e\x90\xfc\x18\xc9\xdb?\x95\xc3v\xaa\xf1\xb9\xfc~" + - "\x92?F\xf2\x8e\xee\x1cv\x00(\x8f\xe0Q\x80\xb1\xe3" + - "$\x7f\x92\xe4\xcb0\x87\xcb\x00\x94'\xf0\x04\xc0\xd8\x93" + - "$\x7f\x9a\xe4\xcb\xdbr\xb8\x1c@9\xc3\xf59I\xf2" + - "g1\"\x90R5\xc9c\xe4Nz\x9c\xabE\xd3\x8e" + - "\xdc\x90\x05\x9d\x04\xfa$[63\xd4J`&\x9e'" + - "\x01b\x06\xd0k\x98fm\xebB~\xbcT\xb9\x10\xb8" + - "\x05dL\xa3T\x8d\xe2\xcbw\xa2\xcd&\xe4+Z\xad" + - "\xd4\x884\xd1\xed\xa2\xeb\x98n\x03\xf2U\xcda\xd5(" + - "\xc3Y\xae\xb1\xd12\xeb\xe3\xc8\xac\xbanh5\x88\xbe" + - "Y\xca\xb72\xae\xabW\x17\x05\x9b\xd0\xech\xf9F\xff" + - "\xb8\xc6\xa3\xab=\x8a\xaek\xa9\x0c\xb9FD\xf5\xc6\x04" + - "\xf9\xac#\x86\xfc\xac\x88\xea\xe7\x05\xcc$\x83\"\xbf_" + - "\xab\xb9\xecr\xca\xa0\x89\xa6T\xe0W\xb3>?'n" + - "\x1f\x8co\x8f.\xa7b\xf1z\x11\xd5\x11\x01\x0f\xdbn" + - "\xa5B\x8f\x0eQ\x98\x0cZ\x10\xc8\xd3\xd9\x09{Dc" + - "\x87\xc0\x1e\x97\x9bv\xa7\x98\xe3\x7f*\x19\x93&\xe5+" + - "I\xab\xdb\xff\xc7\xdd\xa3\xcc\xceP\xc9~\xc9F/\x1a" + - "$\\:\xbf\x8d\x8c\x8f\x97\xe3nT\xf4\xc9\x91\xf3\x02" + - "&\xdau\xa5\x88;@\xe0\xf6\xa3\xe8_\xc7\xc3\xf3z" + - "\x0a\x93\x9b9+d\xfd\xf0\xbf\x89\x87\xe1\xe7I>\x80" + - "\x01KR\xf8\x7f\x01O/`\x97\x94\xec\x87\x7f\x09G" + - "\x93,\"\xa7\xd1\x0f\x7f\x95\x9f_&\xf9\xce\x90\x16(" + - "\xfco\xc7\xb9\x054\"\x89~\xf83\x1e\xce\xd3$w" + - "8-\xa4\xfc\xf0\xdf\x87/\x02\x8c9$?\xc2i!" + - "\xed\x87\xff]\xf8\xf2\x02\x1aY\x16\x84\xff\x03|\xfd1" + - "\x92?\xcei\xe1\xaa\x1cv\x02(\xf3\x9cF\x1e#\xf9" + - "I\x8cj\x9eb\x15\xc4\xaa\xe59\x95\xc6\x97\x19k\x14" + - "!S\xd3\xf7\xb3\x88\xab\xab\xbaV\xdb\xe0j5\xc8\x8f" + - "9Zeo\\c\xd6\xec\x11\xcd\xa8\xda8\xad\xede" + - "\xc4\xf0R2\x07:5{;\xb3\xf4I\xc0\xb8*\x8d" + - "j\x82L\xd94\x9bK\x05^\xdc0\xcb'\x93\xe8\xbb" + - "\xba6S\xaa\xd6\xd8\x10\x86\x95\x81h\xc4\x19F\xa7o" + - "L\xc3@?]\x8f\xeb\xf9\x85y\xb8\x11\xd4\xb9a>" + - "\x1f/4%j6\xd3`\x15g\xc8D\xc3\xd1\x0d\x97" + - "-:\xa02\xed\x1a{Yu\x18\x8d\x8aY\xd5\x8d)" + - "XT`\x8b\x9f\xd4\xfc'\x0a\x98\xf6\xc0\x09\xa3\xc9\xb7" + - "|m\x7f\xe0\x83\x94\x8e\xe5\xfe\xb8\xeb,T\xf8\xae\x82" + - "\xc54\xbbE\x17%~R\x94\x15\xfc\xe0\xa2\xdb\xb2b" + - "\x1a \x1a\x0dc8\xab\x93\xf7\x1d\x04A\xd6%\x8cG" + - "\x9c\x18N4\xe5;-\x10\xe4\x09\x09\x85h|\x8f\xe1" + - "4].\xcd\x81 \x0fK(Fct\x0c\x07Z\xf2" + - "-\x83 \xc8\xeb$/,\xc9\xa1\xe0\xab3\x80^\x18" + - "\xf0\x90\xe7!?\x80^\xd8\xb7cX\xba\x03\x0c\xe0\xe1" + - " \x1d\x0c`rv$~R\xfd\xdc\xba,$n\x9c" + - "\x11Q\xbd'\xe6\xc6\xbb\xe7\xe2F:\xeaY\x1e|\xa6" + - "U'}\x14@}\xdc\xaf\x00\xa3N\xfa,\x95\x8a/" + - "\x89\xa8\xfeD\x88\xf3d\xe8v\xe1\xb8\x05M+l\xa2" + - "\x96\x98\xba\x04\xce\x19Tl\xcd\xb3\x17\xafjN\xf3\x8a" + - "\x0e\xfd\xa3l\x88\x99:9\x90Y\x91\x18\xc8`\xd8\xbe" + - "I\x0b\x88=9\x9eY\xb14W.hF\xc0\x9f\xcf" + - "\x90\xd7\x84\xbf\x17`\xf83\x8f,\x93\xf5\xbb$/l" + - "X0LSd\xbc\xa4\xc9\xae\xb0k\x1bey\xfbr" + - "2@8\x1d\xbet\xf3\xed\xdf\x93!gk\x1a8\xed" + - "ILujf\xd0\xffd\xb6&\xb2\xf5RX\xf9\x0a" + - "\x87\x85g\x8667\xb9\xdf\x9a\xd8\xfd\xa2\xc2\xe0\xee5" + - "\x89\xe9N\xd8\x95\xdc\xbb)p\xca\x93Q\xa1)\x7f\x9d" + - "\x1c\xf5\xa4\x88\xea\xb3\x09\xf7\xfb\xe6\xa6\xb8+\x91\x98e" + - "\x85z.\x98\x97\xd5\xcc\xa9\xcd\xba\xc1l*\xbd\x9aZ" + - "\xe9\x06\xb3\xea\x9a\xc1\x0ct\x88\x8c\\\x8b\x18u!s" + - "\x956$*\xb6\xa5`\x9d0\xf4\x19\xde\xa24\x81\xba" + - "66V\xa6\xa1]^\x073\x16\x04\x8e\x1f7A\x8a" + - "N\xf4\xa0\xa7\x13\xf3\x90\x10H\xf5\xe5`\xce\xb0+\x01" + - "\xe4\x9d\xd4\x83\xee\x14Q\x9d\x16\xd0\xd3\\\xc7\x9chT" + - "5t\xd8F\x8b\xeds\x99dTf\xe3^\x8c\xba\x92" + - "\x8a=\x81\x0d\xaa\x1f7Z\xac\xb0\xcfe\xc9\x05\xe1\x8c" + - "\x17$\xdd\xac.\x1a\xee\xb6(\xd8ne\xbb\xc7\xcc\xca" + - "^\xe6,\x98}\xfb\xd4\x1b>E[\x13+\x18\xbe\x84" + - "\x8d\x02\xa8U\x7fb\x12QR}O<\xdd\x8d(\xc9" + - "\x9d\x8b=j\xe1\x88\xf4\xff'\xa9.5\xd9_PE" + - "\xf9#\xb9)=o\x14\xabU\x8b\x12Y8\xc1NV" + - "\xc3\xf1\x04{\xdd\xdaD9\x1c\x0c\xdf\xa2\xdfp\xfd\x10" + - "\xce\xb8\x86>\x83\xd9\xf8\x87\x9cK\xcfS[NoG" + - "\x0b\xec\xb2\x08$\xfeq\xe5\xd2%d0~\x08*\xf0" + - "\xa6\x02|M\xab\xf2\x7fGP\x81\xdf\x1c\xc4i6\xfe" + - "\xbd7\xb8\xce\x0e\xaa^\x10'\xcd\xc5\x05\xed\xff\x06\x00" + - "\x00\xff\xff\xb0\x8e\x80\xdd" +const schema_db8274f9144abc7e = "x\xda\xacY}\x8c\x1c\xe5y\x7f\x9ey\xf7v|\xe6" + + "\xce{\xe3\xb9\x80}\xd8\xba\xd6\x02%\x10Lq\\Z" + + "\xb8\xb6Y\xdf\x9d\xcf\xb9\xbd\xf8c\xe7\xf6\xce\x80\xb1%" + + "\x8fw\xdf\xdb\x1b{vf=\x1f\xf6\x9d\xe5\xc4`\xd9" + + "\x05\xae\x10l\x82%\xec\x90\x08\xdc\xba|\x08\x1aC@" + + "\x15\xd4\xa4\xa1jK\xda\xa8\"U\x93\xaai\xf3O\x03" + + "VU\xd4\x88\x9a\xa4B\xa9\x80\xa9\x9ew>oo9" + + "\xdbU\xf8\x03\x8f\x9e}\xde\xf7}>\x7f\xcf\xc7\xddv" + + "n\xc9\x06i]\xc7_v\x01h\x8fw\xe4\x83\xdfo" + + "\xbc}\xf6wN\xfd\xe0\x18(}R\xf0\xd5\x0bc\xbd" + + "\xbf\xf2\x8e\xfe\x1b\x00\xae\x1f\xc9\x1fB\xf5\x9e\xbc\x0c\xa0" + + "N\xe6\xb7\x01\x06\xfft\xdb\xe1ww\xff\xe2\xe4\x83\xa0" + + "\xf4a\xca\x99\x93\x01\xd67\xf2s\xa8\x1e\xcf\xcb\xc0\x82" + + "o\xde\xdb\xfb\xf7\xf8\xd4\x87'A\xf9\x1c\x02t \xfd" + + "\xac\xe7\x97J\x80\xeal\xbe\x08\x18\xbc}\xcb\x85\xd7O" + + "|\xe7\x81o\x80\xf6YD\x08\xcf\x9f\xce\xff/\x02\xaa" + + "/\x0a\x86K\x7f\xf2\xf9\xdc\x8bo/\xff\x96`\x08\xce" + + "\xfd\xe3]/\x9f\xf8\xceo\xbc\x07\x93\x92\x8c9\x80\xf5" + + "?\xce;\xc4\xfb\xef\xf9\xff\x00\x0c\x1e\xff\x977\xb66" + + "N\x9e9\x0b\xcag\xe3\xbb\xde\x94%\x09r\xc1\xed\xff" + + "zq\xdb\x96\x97\xa7\x9e\x09\x7f\x09\xe5xU~\x99\x8e" + + "\xfe\x8dL\xcf|\xff`\xcf\xc3\x83\xbf\xfb\xe83\xa0\xf5" + + "aF\x9f\x0eq\xc9\x7f\xcas\xa8\xe2\x12\xfa\xfcX\xee" + + "G\xc0`\xee\xce7\xb6\xff\xe2\x0f\xdd\xe7A[\x8b\xb9" + + "\xe0\xaf\x1fz\xe7\xc0M\xcfM\xbd%\xa4b\x00\xebo" + + "\xef\xfa" + + "\xee\xe6\x97\xe8\xea\x8cQC!.v\x0e\xa0\xfa?\x9d" + + "d\xd7K\x82\xfb\x87\xb7l\xff\xeew\xcf\xd7_j\x15" + + "D\"\xee\x93K\xc7P=\xb7\x94\xb8\x9f^J\xdc\x9f" + + ")\xe1O\xbf\xb7.\xf7\xe7\x91^\x8c\x98&\xafy\x8f" + + "\x1e7\xae!\x86{?z\xf5\xafF\xde\xff\xd1kY" + + "\x07tvI\xe4\x80\xd5]\xa4\xf8\xea\x9f\x0fu[\xef" + + "\x1f\xfd\xde|?\x867\x8dt\x8d\xa1zO\x97pz" + + "\xd7\xb7\x01?|\xfe\x81\x13\xa5w6\xbe\xa5\xf5a\xae" + + "U\x91K]\x87P\xed\xe8\xa6O\xec\x166J\xac\xd2" + + "\xc2.4Y\xb7l/\xaa#\xcb\xe8sp\x99`\x1f" + + "\xbb\xf7\xeb\x8fu\\\xfc\xfa[\xadf\x92\x89\xa7Tp" + + "P\xddU\xa0\xcf{\x0a\xcfH\x80A\xdf\xf9\xdf\xfb\xb3" + + "\xa1\xdaO~\xd0\"7]\xae\x8e,\xff@\xd5\x96\xd3" + + "\xd7\x96\xe5\x07\x01\x83\x07>?{h\xeb\x8ds?n" + + "\xb5\xa9\x10\xfc\xb9\xe5s\xa8\xbe)\xb8\xdf\x10\xdc\xd2E" + + "}\xe5}\xff\xfc\xc5\x9ff\xa2h\xad\xfa3\x84\\\xb0" + + "u\xfb\xbd{;\xbf\xf2\xce;\xd9(\xfaMUX\xfb" + + "v\x95\x8c\xf9\x8a\xf2\x98z\xe1\xe9?}\x97\x1e\x92[" + + "\xad9\xa9\xee@\xd5P\xe9\x93\xabB\x87$\x9a\xdb\xf9" + + "Z\xbfv\x00\xd5\xfd\xd7\x92\\\x8dkI\xae\xdbw\x0f" + + "\xf2\x9dw\xdc\xfd\x1e(}l^n\xbeH\x9co\x10" + + "\xe7\xfa\xd7\xae\x95Q5\xae\x93\x01\x82\xaf\xd5w\xfc\xdd" + + "\xa5\xe1\xa7\xff\xbbmDk\xd7\x0d\xa0\xaa\x13\xdf\xfa]" + + "\xd7\x09\xf3\xaf_\xf7G??\xf5\xc7\xc3\x97\x16\xdc\xfe" + + "\xdc\x8a!T_[Ar\xbc\xba\xe2K\xea\xc5\x15\xe2" + + "\xf2\xafn\xdcv\xe7\x9a7?\xc8Z\xe2\x1fV| " + + "Rq\x05Yb\xea\x8e\xff\xfa\xd2\x8d_\xfb\xdb\x0fZ" + + "\xdc#\x18q\xe5\xcd\xa8*+\xe9\xc6\xee\x95E\xc0\xf7" + + "7}\xebG}\x85\xbe_\xb6\x13t\xddJ\x8a\x93\x95" + + "\"NV\x0aA\xef\xfe\xd9\x99\x83\xc5o\xfc\xf2C\xd2" + + "\x8b\xb5 \xcf\xfe\xbe\x1d\xa8\x1e\xef\xa3\x9b\xef\xef\xa3\xf0" + + "\xdf\xfc\xc2O\xbe8}\xea\xfb\xbfj5\x82p\xc8\xda" + + "\xeb\x8f\xa2:x=q\xff\xc1\xf5\x84\x1f\x87\xdf?=" + + "\xfa\xe8\xce\x17>\xc9ju\xe3\xaa\xd7\x85\x7fW\x91V" + + "{O\x1d\xf6F\x9fx$h\x13t\xeb'W\x0d\xa1" + + "\xcaW\xd1m\xfa\xaa\x83\xb06\xf0|\xcb\xe2\xa6\xd3\xcc" + + "U\x7f+\xfe\xac\xdeZ\xd5\x9bVs`d\xc6p=" + + "\xc3\xaaO\x08z\xb1l\x9bFu\xb6\x8c\xa8u\xa1\x04" + + "\xa0\xac\x1e\x00@T>\xb3\x03\x00%E\x19\x02(\x1a" + + "u\xcbvxP3\xdc\xaamY\x1cX\xd5;\xb2G" + + "7u\xab\xca\x93\x87:\x16>4\xcaM\xd3\xbe\xcbv" + + "\xcc\xda6\xc7\xa8\x1b\xd6\xb0mM\x19u\x802br" + + "L^xl\xd84\xb8\xe5U\xb8s\xc0\xa8\xf2[}" + + "\x97\x87\xe7|G\xf7\x0c\xdb\xbaa\x9c\xbb\xbe\xe9\xb9\x00" + + "Z\x8e\xe5\x00r\x08\xa0t\x0f\x00hK\x18j\xbd\x12" + + "\x16\x1d\xc1\x80=i\xe6\x01b\x0f\xa4o\xe6\x17\xbe\x19" + + "\xda\x82\xde\xe4\xce\xad\xbe\xe5\xf0\xba\xe1z\xdc\x09\xc97" + + "\x14\xcb\xba\xa37\xdc\xec\x83g\x00\xb4\x1e\x86\xda*\x09" + + "\x83\xba\xa3Wy\x99;h\xd8\xb5\xad\xbaeW\x18\xaf" + + "b\x07H\xd8\x91y\xb4\x8d#6\xe9\x86\xc9k\xa1v" + + "\xb7V\xfb\xc5\xbfZ\x0f\xcbu\x05\x81xD\xdf\x01\xa0" + + "\xedf\xa8\x99\x12v\xe3'A/\x15)\xc58\x04\xa0" + + "M3\xd4<\x09\xbb\xa5\x8f\x83^\xe1\xb5\xfdk\x004" + + "\x93\xa16#a7\xfb(\xe8\xa5R\xa0\xf8{\x014" + + "\x8f\xa1v\x9f\x84\x81\xeb7\xc9\xa6.0\xdb\xc1\x9e4" + + "\x94#\xeb\xf0Z\x9d,mA\x91W\xc9\xd0\xd8\x13#" + + "n\xc8 \xd7\xeci\xecIKDt\xcc\xe1\x07\xb8\xe3" + + "\xf22\x14\x1c{f\x16{R\xe4m\xb1z\xf7\xd5Z" + + "=vtrj\xf1\xf3\"4\xab\xde\x0d\xe5\xfe\x05\xce" + + "\";v1\xd4VH\x184\xe9W\xeeq`\x8e\x8b" + + "=i\xe9m\x91\xb6M8\x0f\xd3\xff\x87\xc3W\xca\xd1" + + "-\x8e+\xc2Y\xebM\x1e\xfb\x0a=v\x98\xa1\xf6\xa0" + + "\x84\x0ab\xe8\xb3\xe3\x0e\x80v\x8c\xa1vBB\x94B" + + "\x8f=r\x16@;\xc1P{RB\x85I\xa1\xc3N" + + "\xdfL\xbd\x10C\xed\xbc\x84J\x8e\xf5R\x9b\xa1\xbcH" + + "\xc1v\x9e\xa1vA\xc2\xc0\x0eS\x89\xe4\xf7\xb0\x1b$" + + "\xec\x06\x0c\xaa\xa6\xed\xd7\xa6L\x1d\xfa\x1d^+mL" + + "\xe8\x96\xdf(;\xfc\x80\x81\xb6\xef\x0ez\x1eo\xc8M" + + "\xcf\xc5\xb5\x08\xd0\xc6\xef" + + "\x83\xbe7\xcd-\xcf\xa8\x8a\x07\x17\xf8}M\x1a\x9f\x89" + + "\xcdJ_\xc8\x182\xb6\xd9\x96=\xa9!\xe5}|6" + + "6K?o\xe8\x86\x99x?\xb2\xe6 \xc8_Ny" + + "\x16\xed6\xa2\xb2\x12\x16\x95bh\x9e\x16\xc8\x9c\x03\xd0" + + "\xeec\xa8=\x9c\x11\xf2\xa1\xc7\x00\xb4\x87\x19jOd" + + "\x84<5\x94\xc5L\x16a&Y\xf4I\x86\xda\xb3\x12" + + "b.\x84\xccs\x04\x99\xcf2\xd4^\x91\x04\x0a\x8e\x0e" + + "\x0e\xdb\x16FB\xb8\x001\x06\x06\xd3\\w\xbc=\\" + + "G\xafdy\xdc9\xa0\xa3\x19\xe7\xe0\x11\xcfhp\xdb" + + "\xf7\x92\x9cl\xe83\xa2dcm4<%\xeb\x9e\x8b" + + "\x9d a'\xa5\x80\xcb\x9da\x87\xd7\x90\xbc\xa1\x9be" + + "\x9dy\xd3Wb\xa0\xf9xYhc\x9eCiE\xa1" + + "\xff\xd2\xf9O9>\x00\x92H]\xd2\xb91\x946\x06" + + "\xa2\xa0tP_\xf0X\xda\x01\x88\x82\x92\xa7\x1b\xcf\xa4" + + "\x06\x8fD\x1b\xb5\xa1\x18\xa6D,s1t\xf5\x11\x82" + + "'\x83\xa7zFu\xd6@\xdb\x9a\x10\x06\xc2\xd4BU" + + "\xbb\xd1t\xb8\xeb\xa2a[\x9a\xaf\x9b\x06\xf3f\x93\x83" + + "\x8b\xda\x80r?\xcc\x99m\xcd~\xe1$2\xc2m\xb1" + + "\x11\xd4A\x1c\x03\xa8l@\x86\x95\xcd\x98\x86\x89Z\xc2" + + "!\x80\xcaF\xa2\x971\x8d\x14u\x0b\xf6\x01TF\x89" + + ">\x81\x12b\x18+\xaa\x86\xcf\x03T&\x88\xbc\x1b\xd3" + + "\x12\xab\xee\x12\xd7\xef$\xfa4\xd1;r\xc2|*\xc7" + + "\x9b\x01*\xbb\x89~\x98\xe8yIXP\x9d\xc5\xbd\x00" + + "\x95\x19\xa2\x1f#\xba\xdc\xd1\x8b\xa2\xf1G\x07\xa0r\x1f" + + "\xd1\x1f&\xfa\x92\x15\xbd\xb8\x04@}H\xd0\x1f$\xfa" + + "\xe3D\xef\\\xd9\x8b\x9d\x00\xeaI<\x0aP9A\xf4" + + "'\x89\xbe\x14{q)\x80z\x1a\xcf\x00T\x9e$\xfa" + + "\xb3D\xbf&\xdf\x8b\xd7\x00\xa8\xe7\x84" + + "k\x09!?\xc7P\xfbm\x09\x0b\xd9\xa4\xe8?\xa0\x9b" + + ">\xbf\x926h\xb2\xa5\x14\x84\xddl\x88\xcf\x99\xd7\x87" + + "\xd2\xd7\x93\xc7\xa9Y\xbc\x85\xa16*\xe1\x11\xd7\xafV" + + "I\xe9\xd8\x0aS\xd1\xcc\x00\xfdtw\xc6\x1f\xc98\x1f" + + "\xf9\xe3J\xcbn\x9d{\xe1W\xc9\x9a\xb2\xa9^\xc9z" + + "\xc3\xfd\x7f\x9e\x1e\xe7n\x81Z\xf6\xcbNf\xc9\x80~" + + "\xf9\xfa6:1QN\xc7G\x16\x82c\x16\x17\xc6\xb3" + + "\xb8\x90\xc2\xc2\xdel\xfa\xc7m\x98\xaa\x89<,\x13}" + + "'\xa6}\xb7z\x0f\x9e\x9d\x97\xff\xb9\xc1\x10\x17\xb8\xb8" + + "\xbeF\xf4\xa6\xc0\x05\x0cq\xa1!\xee7\x89>\x93\xc5" + + "\x05\x1f\xe7\xe6\xe3\x02\x8bq\x81\xf2\xf9\x18\xd1O\x08\\" + + "\xc8\x85\xb8\xf0\x08\xbe\x8cq\x87\xc0\xac\xb4\xd2\x18\xf4\x8bmY\x18\x96" + + "\xed\x09\xa3\x7f~=nF\xfdn\\\xd7'\x8a-\x05" + + "\x9b\xcf4y\xd5\x1b\xb6\xd1\xf2\x0c\xcb\xe7\x0b.\xa8N" + + "\xfb\xd6>^\x1bA\xabj\xd7\x0c\xab\x0e\x0b\x1am\xf6" + + "iS{\xa6\x91\x11\xd9\x8c\x99e\xb1r\x13\x95e\x8c" + + "\xca\xb22\x90N\x9f\xc5\xaa8Ut\xb8\xee\xb6\x99\xa6" + + "\xd8\xa7e[1L2z\xad\x87u\x00$\xabW\x8c" + + "wa\xca\xfeC )\x86\x8c\xe9\x0a\x11\xe3\x8d\xa1\xb2" + + "\xcb\x01I\x99\x94QJ6\xde\x18o\xab\x95\xd2\x1cH" + + "\xca\x88\x8c,YSc\xbc\x89R\xee\x1c\x02IY+" + + "\x07qk\x0e\xc5P\x9c\x0d\x18\xc4\x89\x0f\xfd\"\xf57" + + "`\x10\xcf\xef\x18\xb7\xf0\x00\x1b\xf0HT\x166`v" + + "\xe9\xc3>\xad\x8fn\xdf\x1e\x12F\xce0\xd4\x8e\xa5\x18" + + "y\xff\\:P'\xb3\xcb#\xcf\xb7\x9b\xa8\x8f\x02h" + + "O\x84\x9d`2Q\xbfD-\xe3+\x0c\xb5\x1fJi" + + "\xbd\x8c\xc3.\xde\x93\xa0\xed\xc4\xc3\xd4\"\xeb\x92(8" + + "\xa3\xce\xadui\x12\xd4\xeci\xd1\xd9ax\x95\x0b)" + + "bg7)\xcb2\x9b\x14\x8c\xc78y\x1e\xc0g\xf7" + + "*\xcb\x16\xc7\xccyC\x89\xa889\x115\xf1>\x1e" + + "\xe3\xbf\x8c(\x0ay\xbf[\x0e\xe2\xc1\x05\xe3rE\xce" + + "\xcb\xba\xec*\xa7\xb7q\xde\xef^I%\x88\xb7\xaf\x97" + + "\x1f\xc2\xc3w\x0a\x14l\xa1B\xc9\xbd{3\xdb\x1d\xd3" + + "\x8e\xe6\xa0\xc2\xd6L\xd5^\xccV\xa1\xc0q\x03Z\xa0" + + "\xc3-\xe1\xb7&\x0d\xbf\xa4A\xb8\x7fMf\xcb\x13O" + + "'\xc7\xc7\xa2\xa0|*i8\x95oR\xa0>\xc5P" + + "{!\x13~\xcf\x8d\xa5\xd3\x89\xcc\x1d'\x96S\xf6\x9d" + + "\x146M\xbb\xbe\xd9\xb0\xb8K-X\xcbH\xdd\xe4N" + + "C\xb7\xb8\x85\x1e\x81\x91\xef\x10\xa2\xceG\xae\xd2\xc6L" + + "\xe7\xb6\x98\xfa\x95(\xd8\xc3X\x8f\xcakf~<\x9b" + + "\xd9e\xc4\xcak\xafG;\x82\xdd\x19\xe5w\xd1\xfc\xb8" + + "\x93\xa16-a\xa0\xfb\x9e=\xd9\xac\xe9\xe8\xf1M\x0e" + + "\xdf\xefs\xd9\xaa\xce\xa6s\x14M\x14Uw\x12\x9b\xd4" + + "\xfbmrxq\xbf\xcf\xb3\x0c\xf1B\x15d\xc3\xae-" + + "\xd8\xa4\xb6i\xb6\xee\xe2{*vu\x1f\xf7\xe6-\x9a" + + "C\xb8\x8cU\xd1\xc7\xd3mj\xac\x891\x9e\x19\x99b" + + "\x18\xd9O\x01\xd5d\xa8\x1d\xce\xc0\xc8\xec\\\xea\xf0\xf6" + + "\xd5\xf5\xd7S\x10\x17Q\xb2\xed\xb6s\xbc\xc8\xaf(\xd1" + + "\xd2\xbf\x1e\\\xbe\xe5\x8a\xc6\xf5\xa8cmiX\xd7\xb4" + + "k\x97wD\x1d\xeb\x1dQ<\xf7\xa4\x7fw\x8c\x9es" + + "\xa3.\x11\xd8\x94\xbd\xb0\x01\xfc\xbf\x00\x00\x00\xff\xff\xde" + + "T\x04\xc0" func init() { schemas.Register(schema_db8274f9144abc7e, @@ -3973,7 +3729,6 @@ func init() { 0x91f7a001ca145b9d, 0x9b87b390babc2ccf, 0x9e12cfad042ba4f1, - 0xa160eb416f17c28e, 0xa29a916d4ebdd894, 0xa766b24d4fe5da35, 0xa78f37418c1077c8, @@ -3998,10 +3753,8 @@ func init() { 0xf2c122394f447e8e, 0xf2c68e2547ec3866, 0xf41a0f001ad49e46, - 0xf7e406af6bd5236c, 0xf7f49b3f779ae258, 0xf9c895683ed9ac4c, - 0xfc9f83c37bab5621, 0xfeac5c8f4899ef7c, 0xff8d9848747c956a) }