From 7111ab1d9f8ad1aeef96f94797427eba90150230 Mon Sep 17 00:00:00 2001 From: David Robertson Date: Tue, 26 Sep 2023 13:24:26 +0100 Subject: [PATCH 1/2] Check that the homeserver is reachable at startup Closes #286. Tested as follows (see line 4): ``` Sync v3 [0.99.10] (6fe9b18) Debug=true LogLevel= MaxConns=0 Starting prometheus listener on :6060 13:23:33 WRN Could not contact upstream homeserver. Is SYNCV3_SERVER set correctly? error="Get \"http://localhost:8888/_matrix/client/versions\": dial tcp [::1]:8888: connect: connection refused" dest=http://localhost:8888 2023/09/26 13:23:33 OK 20230728114555_device_data_drop_id.sql (7.48ms) 2023/09/26 13:23:33 OK 20230802121023_device_data_jsonb.go (13.85ms) 2023/09/26 13:23:33 OK 20230814183302_cbor_device_data.go (9.01ms) 2023/09/26 13:23:33 OK 20230822180807_bogus_snapshot_cleanup.go (7.64ms) 2023/09/26 13:23:33 OK 20230913120537_events_missing_previous.sql (7.15ms) 2023/09/26 13:23:33 goose: successfully migrated database to version: 20230913120537 13:23:33 INF creating handler 13:23:33 INF retrieved global snapshot from database 13:23:33 INF listening on 0.0.0.0:8844 13:23:33 INF StartV2Pollers num_devices=0 num_fail_decrypt=0 13:23:33 INF StartV2Pollers finished ``` --- sync2/client.go | 31 +++++++++++++++++++++++++++++++ v3.go | 8 ++++++++ 2 files changed, 39 insertions(+) diff --git a/sync2/client.go b/sync2/client.go index bac1d0cf..e072e5b5 100644 --- a/sync2/client.go +++ b/sync2/client.go @@ -20,6 +20,9 @@ var ProxyVersion = "" var HTTP401 error = fmt.Errorf("HTTP 401") type Client interface { + // Versions fetches and parses the list of Matrix versions that the homeserver + // advertises itself as supporting. + Versions(ctx context.Context) (version []string, err error) // WhoAmI asks the homeserver to lookup the access token using the CSAPI /whoami // endpoint. The response must contain a device ID (meaning that we assume the // homeserver supports Matrix >= 1.1.) @@ -49,6 +52,34 @@ func NewHTTPClient(shortTimeout, longTimeout time.Duration, destHomeServer strin } } +func (v *HTTPClient) Versions(ctx context.Context) ([]string, error) { + req, err := http.NewRequestWithContext(ctx, "GET", v.DestinationServer+"/_matrix/client/versions", nil) + if err != nil { + return nil, err + } + req.Header.Set("User-Agent", "sync-v3-proxy-"+ProxyVersion) + res, err := v.Client.Do(req) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("/versions returned HTTP %d", res.StatusCode) + } + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + var parsedRes struct { + Result []string `json:"versions"` + } + err = json.Unmarshal(body, &parsedRes) + if err != nil { + return nil, fmt.Errorf("could not parse /versions response: %w", err) + } + return parsedRes.Result, nil +} + // Return sync2.HTTP401 if this request returns 401 func (v *HTTPClient) WhoAmI(ctx context.Context, accessToken string) (string, string, error) { req, err := http.NewRequestWithContext(ctx, "GET", v.DestinationServer+"/_matrix/client/r0/account/whoami", nil) diff --git a/v3.go b/v3.go index 068a7b11..8040fc6b 100644 --- a/v3.go +++ b/v3.go @@ -1,6 +1,7 @@ package slidingsync import ( + "context" "embed" "encoding/json" "fmt" @@ -86,6 +87,13 @@ func allowCORS(next http.Handler) http.HandlerFunc { func Setup(destHomeserver, postgresURI, secret string, opts Opts) (*handler2.Handler, http.Handler) { // Setup shared DB and HTTP client v2Client := sync2.NewHTTPClient(opts.HTTPTimeout, opts.HTTPLongTimeout, destHomeserver) + + // Sanity check that we can contact the upstream homeserver. + _, err := v2Client.Versions(context.Background()) + if err != nil { + logger.Warn().Err(err).Str("dest", destHomeserver).Msg("Could not contact upstream homeserver. Is SYNCV3_SERVER set correctly?") + } + db, err := sqlx.Open("postgres", postgresURI) if err != nil { sentry.CaptureException(err) From a28e419d5d09b3c282bbba5edfeb9109e89777ea Mon Sep 17 00:00:00 2001 From: David Robertson Date: Tue, 26 Sep 2023 13:35:24 +0100 Subject: [PATCH 2/2] Update mockClient to match new interface --- sync2/poller_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sync2/poller_test.go b/sync2/poller_test.go index 27ee4aba..d2056f3c 100644 --- a/sync2/poller_test.go +++ b/sync2/poller_test.go @@ -1188,6 +1188,9 @@ type mockClient struct { fn func(authHeader, since string) (*SyncResponse, int, error) } +func (c *mockClient) Versions(ctx context.Context) ([]string, error) { + return []string{"v1.1"}, nil +} func (c *mockClient) DoSyncV2(ctx context.Context, authHeader, since string, isFirst, toDeviceOnly bool) (*SyncResponse, int, error) { return c.fn(authHeader, since) }