Merge branch 'main' of github.com:matrix-org/dendrite into s7evink/devicelistcleanup

This commit is contained in:
Till Faelligen 2022-12-08 14:00:12 +01:00
commit fe9ac2511b
No known key found for this signature in database
GPG key ID: ACCDC9606D472758
68 changed files with 1417 additions and 450 deletions

20
.github/codecov.yaml vendored Normal file
View file

@ -0,0 +1,20 @@
flag_management:
default_rules:
carryforward: true
coverage:
status:
project:
default:
target: auto
threshold: 0%
base: auto
flags:
- unittests
patch:
default:
target: 75%
threshold: 0%
base: auto
flags:
- unittests

View file

@ -68,7 +68,7 @@ jobs:
# run go test with different go versions # run go test with different go versions
test: test:
timeout-minutes: 5 timeout-minutes: 10
name: Unit tests (Go ${{ matrix.go }}) name: Unit tests (Go ${{ matrix.go }})
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Service containers to run with `container-job` # Service containers to run with `container-job`
@ -94,14 +94,22 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
go: ["1.18", "1.19"] go: ["1.19"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Setup go - name: Setup go
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
cache: true - uses: actions/cache@v3
# manually set up caches, as they otherwise clash with different steps using setup-go with cache=true
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go${{ matrix.go }}-unit-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go${{ matrix.go }}-unit-
- name: Set up gotestfmt - name: Set up gotestfmt
uses: gotesttools/gotestfmt-action@v2 uses: gotesttools/gotestfmt-action@v2
with: with:
@ -194,6 +202,66 @@ jobs:
with: with:
jobs: ${{ toJSON(needs) }} jobs: ${{ toJSON(needs) }}
# run go test with different go versions
integration:
timeout-minutes: 20
needs: initial-tests-done
name: Integration tests (Go ${{ matrix.go }})
runs-on: ubuntu-latest
# Service containers to run with `container-job`
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres:13-alpine
# Provide the password for postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dendrite
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
strategy:
fail-fast: false
matrix:
go: ["1.19"]
steps:
- uses: actions/checkout@v3
- name: Setup go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Set up gotestfmt
uses: gotesttools/gotestfmt-action@v2
with:
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go${{ matrix.go }}-test-race-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go${{ matrix.go }}-test-race-
- run: go test -race -json -v -coverpkg=./... -coverprofile=cover.out $(go list ./... | grep -v /cmd/dendrite*) 2>&1 | gotestfmt
env:
POSTGRES_HOST: localhost
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dendrite
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
flags: unittests
# run database upgrade tests # run database upgrade tests
upgrade_test: upgrade_test:
name: Upgrade tests name: Upgrade tests
@ -404,6 +472,7 @@ jobs:
upgrade_test_direct, upgrade_test_direct,
sytest, sytest,
complement, complement,
integration
] ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ !cancelled() }} # Run this even if prior jobs were skipped if: ${{ !cancelled() }} # Run this even if prior jobs were skipped

View file

@ -68,18 +68,6 @@ jobs:
${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }} ${{ env.DOCKER_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }}
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }} ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }}
format: "sarif"
output: "trivy-results.sarif"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: "trivy-results.sarif"
- name: Build release monolith image - name: Build release monolith image
if: github.event_name == 'release' # Only for GitHub releases if: github.event_name == 'release' # Only for GitHub releases
id: docker_build_monolith_release id: docker_build_monolith_release
@ -98,6 +86,18 @@ jobs:
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:latest ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:latest
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ env.RELEASE_VERSION }} ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ env.RELEASE_VERSION }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:${{ github.ref_name }}
format: "sarif"
output: "trivy-results.sarif"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: "trivy-results.sarif"
polylith: polylith:
name: Polylith image name: Polylith image
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -148,18 +148,6 @@ jobs:
${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }} ${{ env.DOCKER_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }}
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }} ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }}
format: "sarif"
output: "trivy-results.sarif"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: "trivy-results.sarif"
- name: Build release polylith image - name: Build release polylith image
if: github.event_name == 'release' # Only for GitHub releases if: github.event_name == 'release' # Only for GitHub releases
id: docker_build_polylith_release id: docker_build_polylith_release
@ -178,6 +166,18 @@ jobs:
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:latest ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:latest
ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ env.RELEASE_VERSION }} ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ env.RELEASE_VERSION }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-polylith:${{ github.ref_name }}
format: "sarif"
output: "trivy-results.sarif"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: "trivy-results.sarif"
demo-pinecone: demo-pinecone:
name: Pinecone demo image name: Pinecone demo image
runs-on: ubuntu-latest runs-on: ubuntu-latest

View file

@ -10,79 +10,9 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
# run go test with different go versions
test:
timeout-minutes: 20
name: Unit tests (Go ${{ matrix.go }})
runs-on: ubuntu-latest
# Service containers to run with `container-job`
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres:13-alpine
# Provide the password for postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dendrite
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
strategy:
fail-fast: false
matrix:
go: ["1.18", "1.19"]
steps:
- uses: actions/checkout@v3
- name: Setup go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Set up gotestfmt
uses: gotesttools/gotestfmt-action@v2
with:
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go${{ matrix.go }}-test-race-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go${{ matrix.go }}-test-race-
- run: go test -race -json -v -coverpkg=./... -coverprofile=cover.out $(go list ./... | grep -v /cmd/dendrite*) 2>&1 | gotestfmt
env:
POSTGRES_HOST: localhost
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dendrite
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
# Dummy step to gate other tests on without repeating the whole list
initial-tests-done:
name: Initial tests passed
needs: [test]
runs-on: ubuntu-latest
if: ${{ !cancelled() }} # Run this even if prior jobs were skipped
steps:
- name: Check initial tests passed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
# run Sytest in different variations # run Sytest in different variations
sytest: sytest:
timeout-minutes: 60 timeout-minutes: 60
needs: initial-tests-done
name: "Sytest (${{ matrix.label }})" name: "Sytest (${{ matrix.label }})"
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
@ -104,13 +34,23 @@ jobs:
image: matrixdotorg/sytest-dendrite:latest image: matrixdotorg/sytest-dendrite:latest
volumes: volumes:
- ${{ github.workspace }}:/src - ${{ github.workspace }}:/src
- /root/.cache/go-build:/github/home/.cache/go-build
- /root/.cache/go-mod:/gopath/pkg/mod
env: env:
POSTGRES: ${{ matrix.postgres && 1}} POSTGRES: ${{ matrix.postgres && 1}}
API: ${{ matrix.api && 1 }} API: ${{ matrix.api && 1 }}
SYTEST_BRANCH: ${{ github.head_ref }} SYTEST_BRANCH: ${{ github.head_ref }}
RACE_DETECTION: 1 RACE_DETECTION: 1
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/go-build
/gopath/pkg/mod
key: ${{ runner.os }}-go-sytest-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-sytest-
- name: Run Sytest - name: Run Sytest
run: /bootstrap.sh dendrite run: /bootstrap.sh dendrite
working-directory: /src working-directory: /src

View file

@ -1,5 +1,28 @@
# Changelog # Changelog
## Dendrite 0.10.8 (2022-11-29)
### Features
* The built-in NATS Server has been updated to version 2.9.8
* A number of under-the-hood changes have been merged for future virtual hosting support in Dendrite (running multiple domain names on the same Dendrite deployment)
### Fixes
* Event auth handling of invites has been refactored, which should fix some edge cases being handled incorrectly
* Fix a bug when returning an empty protocol list, which could cause Element to display "The homeserver may be too old to support third party networks" when opening the public room directory
* The sync API will no longer filter out the user's own membership when using lazy-loading
* Dendrite will now correctly detect JetStream consumers being deleted, stopping the consumer goroutine as needed
* A panic in the federation API where the server list could go out of bounds has been fixed
* Blacklisted servers will now be excluded when querying joined servers, which improves CPU usage and performs less unnecessary outbound requests
* A database writer will now be used to assign state key NIDs when requesting NIDs that may not exist yet
* Dendrite will now correctly move local aliases for an upgraded room when the room is upgraded remotely
* Dendrite will now correctly move account data for an upgraded room when the room is upgraded remotely
* Missing state key NIDs will now be allocated on request rather than returning an error
* Guest access is now correctly denied on a number of endpoints
* Presence information will now be correctly sent for new private chats
* A number of unspecced fields have been removed from outbound `/send` transactions
## Dendrite 0.10.7 (2022-11-04) ## Dendrite 0.10.7 (2022-11-04)
### Features ### Features

View file

@ -24,6 +24,8 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/matrix-org/gomatrixserverlib"
appserviceAPI "github.com/matrix-org/dendrite/appservice/api" appserviceAPI "github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/appservice/consumers" "github.com/matrix-org/dendrite/appservice/consumers"
"github.com/matrix-org/dendrite/appservice/inthttp" "github.com/matrix-org/dendrite/appservice/inthttp"
@ -32,12 +34,11 @@ import (
"github.com/matrix-org/dendrite/setup/base" "github.com/matrix-org/dendrite/setup/base"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
userapi "github.com/matrix-org/dendrite/userapi/api" userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
) )
// AddInternalRoutes registers HTTP handlers for internal API calls // AddInternalRoutes registers HTTP handlers for internal API calls
func AddInternalRoutes(router *mux.Router, queryAPI appserviceAPI.AppServiceInternalAPI) { func AddInternalRoutes(router *mux.Router, queryAPI appserviceAPI.AppServiceInternalAPI, enableMetrics bool) {
inthttp.AddRoutes(queryAPI, router) inthttp.AddRoutes(queryAPI, router, enableMetrics)
} }
// NewInternalAPI returns a concerete implementation of the internal API. Callers // NewInternalAPI returns a concerete implementation of the internal API. Callers

View file

@ -0,0 +1,223 @@
package appservice_test
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"reflect"
"regexp"
"strings"
"testing"
"github.com/gorilla/mux"
"github.com/matrix-org/dendrite/appservice"
"github.com/matrix-org/dendrite/appservice/api"
"github.com/matrix-org/dendrite/appservice/inthttp"
"github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/roomserver"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/userapi"
"github.com/matrix-org/dendrite/test/testrig"
)
func TestAppserviceInternalAPI(t *testing.T) {
// Set expected results
existingProtocol := "irc"
wantLocationResponse := []api.ASLocationResponse{{Protocol: existingProtocol, Fields: []byte("{}")}}
wantUserResponse := []api.ASUserResponse{{Protocol: existingProtocol, Fields: []byte("{}")}}
wantProtocolResponse := api.ASProtocolResponse{Instances: []api.ProtocolInstance{{Fields: []byte("{}")}}}
wantProtocolResult := map[string]api.ASProtocolResponse{
existingProtocol: wantProtocolResponse,
}
// create a dummy AS url, handling some cases
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case strings.Contains(r.URL.Path, "location"):
// Check if we've got an existing protocol, if so, return a proper response.
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantLocationResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode([]api.ASLocationResponse{}); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
case strings.Contains(r.URL.Path, "user"):
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantUserResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode([]api.UserResponse{}); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
case strings.Contains(r.URL.Path, "protocol"):
if r.URL.Path[len(r.URL.Path)-len(existingProtocol):] == existingProtocol {
if err := json.NewEncoder(w).Encode(wantProtocolResponse); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
}
if err := json.NewEncoder(w).Encode(nil); err != nil {
t.Fatalf("failed to encode response: %s", err)
}
return
default:
t.Logf("hit location: %s", r.URL.Path)
}
}))
// The test cases to run
runCases := func(t *testing.T, testAPI api.AppServiceInternalAPI) {
t.Run("UserIDExists", func(t *testing.T) {
testUserIDExists(t, testAPI, "@as-testing:test", true)
testUserIDExists(t, testAPI, "@as1-testing:test", false)
})
t.Run("AliasExists", func(t *testing.T) {
testAliasExists(t, testAPI, "@asroom-testing:test", true)
testAliasExists(t, testAPI, "@asroom1-testing:test", false)
})
t.Run("Locations", func(t *testing.T) {
testLocations(t, testAPI, existingProtocol, wantLocationResponse)
testLocations(t, testAPI, "abc", nil)
})
t.Run("User", func(t *testing.T) {
testUser(t, testAPI, existingProtocol, wantUserResponse)
testUser(t, testAPI, "abc", nil)
})
t.Run("Protocols", func(t *testing.T) {
testProtocol(t, testAPI, existingProtocol, wantProtocolResult)
testProtocol(t, testAPI, existingProtocol, wantProtocolResult) // tests the cache
testProtocol(t, testAPI, "", wantProtocolResult) // tests getting all protocols
testProtocol(t, testAPI, "abc", nil)
})
}
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
base, closeBase := testrig.CreateBaseDendrite(t, dbType)
defer closeBase()
// Create a dummy application service
base.Cfg.AppServiceAPI.Derived.ApplicationServices = []config.ApplicationService{
{
ID: "someID",
URL: srv.URL,
ASToken: "",
HSToken: "",
SenderLocalpart: "senderLocalPart",
NamespaceMap: map[string][]config.ApplicationServiceNamespace{
"users": {{RegexpObject: regexp.MustCompile("as-.*")}},
"aliases": {{RegexpObject: regexp.MustCompile("asroom-.*")}},
},
Protocols: []string{existingProtocol},
},
}
// Create required internal APIs
rsAPI := roomserver.NewInternalAPI(base)
usrAPI := userapi.NewInternalAPI(base, &base.Cfg.UserAPI, nil, nil, rsAPI, nil)
asAPI := appservice.NewInternalAPI(base, usrAPI, rsAPI)
// Finally execute the tests
t.Run("HTTP API", func(t *testing.T) {
router := mux.NewRouter().PathPrefix(httputil.InternalPathPrefix).Subrouter()
appservice.AddInternalRoutes(router, asAPI, base.EnableMetrics)
apiURL, cancel := test.ListenAndServe(t, router, false)
defer cancel()
asHTTPApi, err := inthttp.NewAppserviceClient(apiURL, &http.Client{})
if err != nil {
t.Fatalf("failed to create HTTP client: %s", err)
}
runCases(t, asHTTPApi)
})
t.Run("Monolith", func(t *testing.T) {
runCases(t, asAPI)
})
})
}
func testUserIDExists(t *testing.T, asAPI api.AppServiceInternalAPI, userID string, wantExists bool) {
ctx := context.Background()
userResp := &api.UserIDExistsResponse{}
if err := asAPI.UserIDExists(ctx, &api.UserIDExistsRequest{
UserID: userID,
}, userResp); err != nil {
t.Errorf("failed to get userID: %s", err)
}
if userResp.UserIDExists != wantExists {
t.Errorf("unexpected result for UserIDExists(%s): %v, expected %v", userID, userResp.UserIDExists, wantExists)
}
}
func testAliasExists(t *testing.T, asAPI api.AppServiceInternalAPI, alias string, wantExists bool) {
ctx := context.Background()
aliasResp := &api.RoomAliasExistsResponse{}
if err := asAPI.RoomAliasExists(ctx, &api.RoomAliasExistsRequest{
Alias: alias,
}, aliasResp); err != nil {
t.Errorf("failed to get alias: %s", err)
}
if aliasResp.AliasExists != wantExists {
t.Errorf("unexpected result for RoomAliasExists(%s): %v, expected %v", alias, aliasResp.AliasExists, wantExists)
}
}
func testLocations(t *testing.T, asAPI api.AppServiceInternalAPI, proto string, wantResult []api.ASLocationResponse) {
ctx := context.Background()
locationResp := &api.LocationResponse{}
if err := asAPI.Locations(ctx, &api.LocationRequest{
Protocol: proto,
}, locationResp); err != nil {
t.Errorf("failed to get locations: %s", err)
}
if !reflect.DeepEqual(locationResp.Locations, wantResult) {
t.Errorf("unexpected result for Locations(%s): %+v, expected %+v", proto, locationResp.Locations, wantResult)
}
}
func testUser(t *testing.T, asAPI api.AppServiceInternalAPI, proto string, wantResult []api.ASUserResponse) {
ctx := context.Background()
userResp := &api.UserResponse{}
if err := asAPI.User(ctx, &api.UserRequest{
Protocol: proto,
}, userResp); err != nil {
t.Errorf("failed to get user: %s", err)
}
if !reflect.DeepEqual(userResp.Users, wantResult) {
t.Errorf("unexpected result for User(%s): %+v, expected %+v", proto, userResp.Users, wantResult)
}
}
func testProtocol(t *testing.T, asAPI api.AppServiceInternalAPI, proto string, wantResult map[string]api.ASProtocolResponse) {
ctx := context.Background()
protoResp := &api.ProtocolResponse{}
if err := asAPI.Protocols(ctx, &api.ProtocolRequest{
Protocol: proto,
}, protoResp); err != nil {
t.Errorf("failed to get Protocols: %s", err)
}
if !reflect.DeepEqual(protoResp.Protocols, wantResult) {
t.Errorf("unexpected result for Protocols(%s): %+v, expected %+v", proto, protoResp.Protocols[proto], wantResult)
}
}

View file

@ -8,29 +8,29 @@ import (
) )
// AddRoutes adds the AppServiceQueryAPI handlers to the http.ServeMux. // AddRoutes adds the AppServiceQueryAPI handlers to the http.ServeMux.
func AddRoutes(a api.AppServiceInternalAPI, internalAPIMux *mux.Router) { func AddRoutes(a api.AppServiceInternalAPI, internalAPIMux *mux.Router, enableMetrics bool) {
internalAPIMux.Handle( internalAPIMux.Handle(
AppServiceRoomAliasExistsPath, AppServiceRoomAliasExistsPath,
httputil.MakeInternalRPCAPI("AppserviceRoomAliasExists", a.RoomAliasExists), httputil.MakeInternalRPCAPI("AppserviceRoomAliasExists", enableMetrics, a.RoomAliasExists),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
AppServiceUserIDExistsPath, AppServiceUserIDExistsPath,
httputil.MakeInternalRPCAPI("AppserviceUserIDExists", a.UserIDExists), httputil.MakeInternalRPCAPI("AppserviceUserIDExists", enableMetrics, a.UserIDExists),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
AppServiceProtocolsPath, AppServiceProtocolsPath,
httputil.MakeInternalRPCAPI("AppserviceProtocols", a.Protocols), httputil.MakeInternalRPCAPI("AppserviceProtocols", enableMetrics, a.Protocols),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
AppServiceLocationsPath, AppServiceLocationsPath,
httputil.MakeInternalRPCAPI("AppserviceLocations", a.Locations), httputil.MakeInternalRPCAPI("AppserviceLocations", enableMetrics, a.Locations),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
AppServiceUserPath, AppServiceUserPath,
httputil.MakeInternalRPCAPI("AppserviceUser", a.User), httputil.MakeInternalRPCAPI("AppserviceUser", enableMetrics, a.User),
) )
} }

View file

@ -336,6 +336,7 @@ func (m *DendriteMonolith) Start() {
} }
base := base.NewBaseDendrite(cfg, "Monolith") base := base.NewBaseDendrite(cfg, "Monolith")
base.ConfigureAdminEndpoints()
defer base.Close() // nolint: errcheck defer base.Close() // nolint: errcheck
federation := conn.CreateFederationClient(base, m.PineconeQUIC) federation := conn.CreateFederationClient(base, m.PineconeQUIC)
@ -382,6 +383,8 @@ func (m *DendriteMonolith) Start() {
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux) httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux)
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux) httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux) httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
httpRouter.PathPrefix(httputil.DendriteAdminPathPrefix).Handler(base.DendriteAdminMux)
httpRouter.PathPrefix(httputil.SynapseAdminPathPrefix).Handler(base.SynapseAdminMux)
httpRouter.HandleFunc("/pinecone", m.PineconeRouter.ManholeHandler) httpRouter.HandleFunc("/pinecone", m.PineconeRouter.ManholeHandler)
pMux := mux.NewRouter().SkipClean(true).UseEncodedPath() pMux := mux.NewRouter().SkipClean(true).UseEncodedPath()

View file

@ -150,6 +150,7 @@ func (m *DendriteMonolith) Start() {
} }
base := base.NewBaseDendrite(cfg, "Monolith") base := base.NewBaseDendrite(cfg, "Monolith")
base.ConfigureAdminEndpoints()
m.processContext = base.ProcessContext m.processContext = base.ProcessContext
defer base.Close() // nolint: errcheck defer base.Close() // nolint: errcheck
@ -196,6 +197,8 @@ func (m *DendriteMonolith) Start() {
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux) httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux)
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux) httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux) httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
httpRouter.PathPrefix(httputil.DendriteAdminPathPrefix).Handler(base.DendriteAdminMux)
httpRouter.PathPrefix(httputil.SynapseAdminPathPrefix).Handler(base.SynapseAdminMux)
yggRouter := mux.NewRouter() yggRouter := mux.NewRouter()
yggRouter.PathPrefix(httputil.PublicFederationPathPrefix).Handler(base.PublicFederationAPIMux) yggRouter.PathPrefix(httputil.PublicFederationPathPrefix).Handler(base.PublicFederationAPIMux)

View file

@ -36,9 +36,15 @@ func Protocols(req *http.Request, asAPI appserviceAPI.AppServiceInternalAPI, dev
return jsonerror.InternalServerError() return jsonerror.InternalServerError()
} }
if !resp.Exists { if !resp.Exists {
if protocol != "" {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound("The protocol is unknown."),
}
}
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotFound, Code: http.StatusOK,
JSON: jsonerror.NotFound("The protocol is unknown."), JSON: struct{}{},
} }
} }
if protocol != "" { if protocol != "" {

View file

@ -177,7 +177,7 @@ func sharedSecretRegister(sharedSecret, serverURL, localpart, password string, a
defer regResp.Body.Close() // nolint: errcheck defer regResp.Body.Close() // nolint: errcheck
if regResp.StatusCode < 200 || regResp.StatusCode >= 300 { if regResp.StatusCode < 200 || regResp.StatusCode >= 300 {
body, _ = io.ReadAll(regResp.Body) body, _ = io.ReadAll(regResp.Body)
return "", fmt.Errorf(gjson.GetBytes(body, "error").Str) return "", fmt.Errorf("got HTTP %d error from server: %s", regResp.StatusCode, string(body))
} }
r, err := io.ReadAll(regResp.Body) r, err := io.ReadAll(regResp.Body)
if err != nil { if err != nil {

View file

@ -155,6 +155,7 @@ func main() {
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID) cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
base := base.NewBaseDendrite(cfg, "Monolith") base := base.NewBaseDendrite(cfg, "Monolith")
base.ConfigureAdminEndpoints()
defer base.Close() // nolint: errcheck defer base.Close() // nolint: errcheck
pineconeEventChannel := make(chan pineconeEvents.Event) pineconeEventChannel := make(chan pineconeEvents.Event)
@ -248,6 +249,8 @@ func main() {
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux) httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux)
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux) httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux) httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
httpRouter.PathPrefix(httputil.DendriteAdminPathPrefix).Handler(base.DendriteAdminMux)
httpRouter.PathPrefix(httputil.SynapseAdminPathPrefix).Handler(base.SynapseAdminMux)
httpRouter.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { httpRouter.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
c, err := wsUpgrader.Upgrade(w, r, nil) c, err := wsUpgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {

View file

@ -144,6 +144,7 @@ func main() {
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID) cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
base := base.NewBaseDendrite(cfg, "Monolith") base := base.NewBaseDendrite(cfg, "Monolith")
base.ConfigureAdminEndpoints()
defer base.Close() // nolint: errcheck defer base.Close() // nolint: errcheck
ygg, err := yggconn.Setup(sk, *instanceName, ".", *instancePeer, *instanceListen) ygg, err := yggconn.Setup(sk, *instanceName, ".", *instancePeer, *instanceListen)
@ -199,6 +200,8 @@ func main() {
httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux) httpRouter.PathPrefix(httputil.InternalPathPrefix).Handler(base.InternalAPIMux)
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux) httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux) httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
httpRouter.PathPrefix(httputil.DendriteAdminPathPrefix).Handler(base.DendriteAdminMux)
httpRouter.PathPrefix(httputil.SynapseAdminPathPrefix).Handler(base.SynapseAdminMux)
embed.Embed(httpRouter, *instancePort, "Yggdrasil Demo") embed.Embed(httpRouter, *instancePort, "Yggdrasil Demo")
yggRouter := mux.NewRouter().SkipClean(true).UseEncodedPath() yggRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()

View file

@ -18,6 +18,8 @@ import (
"flag" "flag"
"os" "os"
"github.com/sirupsen/logrus"
"github.com/matrix-org/dendrite/appservice" "github.com/matrix-org/dendrite/appservice"
"github.com/matrix-org/dendrite/federationapi" "github.com/matrix-org/dendrite/federationapi"
"github.com/matrix-org/dendrite/keyserver" "github.com/matrix-org/dendrite/keyserver"
@ -29,7 +31,6 @@ import (
"github.com/matrix-org/dendrite/setup/mscs" "github.com/matrix-org/dendrite/setup/mscs"
"github.com/matrix-org/dendrite/userapi" "github.com/matrix-org/dendrite/userapi"
uapi "github.com/matrix-org/dendrite/userapi/api" uapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/sirupsen/logrus"
) )
var ( var (
@ -75,7 +76,7 @@ func main() {
// call functions directly on the impl unless running in HTTP mode // call functions directly on the impl unless running in HTTP mode
rsAPI := rsImpl rsAPI := rsImpl
if base.UseHTTPAPIs { if base.UseHTTPAPIs {
roomserver.AddInternalRoutes(base.InternalAPIMux, rsImpl) roomserver.AddInternalRoutes(base.InternalAPIMux, rsImpl, base.EnableMetrics)
rsAPI = base.RoomserverHTTPClient() rsAPI = base.RoomserverHTTPClient()
} }
if traceInternal { if traceInternal {
@ -89,7 +90,7 @@ func main() {
) )
fsImplAPI := fsAPI fsImplAPI := fsAPI
if base.UseHTTPAPIs { if base.UseHTTPAPIs {
federationapi.AddInternalRoutes(base.InternalAPIMux, fsAPI) federationapi.AddInternalRoutes(base.InternalAPIMux, fsAPI, base.EnableMetrics)
fsAPI = base.FederationAPIHTTPClient() fsAPI = base.FederationAPIHTTPClient()
} }
keyRing := fsAPI.KeyRing() keyRing := fsAPI.KeyRing()
@ -97,7 +98,7 @@ func main() {
keyImpl := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI, rsAPI) keyImpl := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI, rsAPI)
keyAPI := keyImpl keyAPI := keyImpl
if base.UseHTTPAPIs { if base.UseHTTPAPIs {
keyserver.AddInternalRoutes(base.InternalAPIMux, keyAPI) keyserver.AddInternalRoutes(base.InternalAPIMux, keyAPI, base.EnableMetrics)
keyAPI = base.KeyServerHTTPClient() keyAPI = base.KeyServerHTTPClient()
} }
@ -105,7 +106,7 @@ func main() {
userImpl := userapi.NewInternalAPI(base, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI, rsAPI, pgClient) userImpl := userapi.NewInternalAPI(base, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI, rsAPI, pgClient)
userAPI := userImpl userAPI := userImpl
if base.UseHTTPAPIs { if base.UseHTTPAPIs {
userapi.AddInternalRoutes(base.InternalAPIMux, userAPI) userapi.AddInternalRoutes(base.InternalAPIMux, userAPI, base.EnableMetrics)
userAPI = base.UserAPIClient() userAPI = base.UserAPIClient()
} }
if traceInternal { if traceInternal {
@ -119,7 +120,7 @@ func main() {
// before the listeners are up. // before the listeners are up.
asAPI := appservice.NewInternalAPI(base, userImpl, rsAPI) asAPI := appservice.NewInternalAPI(base, userImpl, rsAPI)
if base.UseHTTPAPIs { if base.UseHTTPAPIs {
appservice.AddInternalRoutes(base.InternalAPIMux, asAPI) appservice.AddInternalRoutes(base.InternalAPIMux, asAPI, base.EnableMetrics)
asAPI = base.AppserviceHTTPClient() asAPI = base.AppserviceHTTPClient()
} }

View file

@ -26,7 +26,7 @@ func Appservice(base *base.BaseDendrite, cfg *config.Dendrite) {
rsAPI := base.RoomserverHTTPClient() rsAPI := base.RoomserverHTTPClient()
intAPI := appservice.NewInternalAPI(base, userAPI, rsAPI) intAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
appservice.AddInternalRoutes(base.InternalAPIMux, intAPI) appservice.AddInternalRoutes(base.InternalAPIMux, intAPI, base.EnableMetrics)
base.SetupAndServeHTTP( base.SetupAndServeHTTP(
base.Cfg.AppServiceAPI.InternalAPI.Listen, // internal listener base.Cfg.AppServiceAPI.InternalAPI.Listen, // internal listener

View file

@ -34,7 +34,7 @@ func FederationAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
rsAPI, fsAPI, keyAPI, nil, rsAPI, fsAPI, keyAPI, nil,
) )
federationapi.AddInternalRoutes(base.InternalAPIMux, fsAPI) federationapi.AddInternalRoutes(base.InternalAPIMux, fsAPI, base.EnableMetrics)
base.SetupAndServeHTTP( base.SetupAndServeHTTP(
base.Cfg.FederationAPI.InternalAPI.Listen, base.Cfg.FederationAPI.InternalAPI.Listen,

View file

@ -26,7 +26,7 @@ func KeyServer(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
intAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI, rsAPI) intAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI, rsAPI)
intAPI.SetUserAPI(base.UserAPIClient()) intAPI.SetUserAPI(base.UserAPIClient())
keyserver.AddInternalRoutes(base.InternalAPIMux, intAPI) keyserver.AddInternalRoutes(base.InternalAPIMux, intAPI, base.EnableMetrics)
base.SetupAndServeHTTP( base.SetupAndServeHTTP(
base.Cfg.KeyServer.InternalAPI.Listen, // internal listener base.Cfg.KeyServer.InternalAPI.Listen, // internal listener

View file

@ -26,7 +26,7 @@ func RoomServer(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
rsAPI := roomserver.NewInternalAPI(base) rsAPI := roomserver.NewInternalAPI(base)
rsAPI.SetFederationAPI(fsAPI, fsAPI.KeyRing()) rsAPI.SetFederationAPI(fsAPI, fsAPI.KeyRing())
rsAPI.SetAppserviceAPI(asAPI) rsAPI.SetAppserviceAPI(asAPI)
roomserver.AddInternalRoutes(base.InternalAPIMux, rsAPI) roomserver.AddInternalRoutes(base.InternalAPIMux, rsAPI, base.EnableMetrics)
base.SetupAndServeHTTP( base.SetupAndServeHTTP(
base.Cfg.RoomServer.InternalAPI.Listen, // internal listener base.Cfg.RoomServer.InternalAPI.Listen, // internal listener

View file

@ -27,7 +27,7 @@ func UserAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
base.PushGatewayHTTPClient(), base.PushGatewayHTTPClient(),
) )
userapi.AddInternalRoutes(base.InternalAPIMux, userAPI) userapi.AddInternalRoutes(base.InternalAPIMux, userAPI, base.EnableMetrics)
base.SetupAndServeHTTP( base.SetupAndServeHTTP(
base.Cfg.UserAPI.InternalAPI.Listen, // internal listener base.Cfg.UserAPI.InternalAPI.Listen, // internal listener

View file

@ -7,6 +7,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -61,6 +62,7 @@ COPY . .
RUN go build ./cmd/dendrite-monolith-server RUN go build ./cmd/dendrite-monolith-server
RUN go build ./cmd/generate-keys RUN go build ./cmd/generate-keys
RUN go build ./cmd/generate-config RUN go build ./cmd/generate-config
RUN go build ./cmd/create-account
RUN ./generate-config --ci > dendrite.yaml RUN ./generate-config --ci > dendrite.yaml
RUN ./generate-keys --private-key matrix_key.pem --tls-cert server.crt --tls-key server.key RUN ./generate-keys --private-key matrix_key.pem --tls-cert server.crt --tls-key server.key
@ -104,6 +106,7 @@ COPY . .
RUN go build ./cmd/dendrite-monolith-server RUN go build ./cmd/dendrite-monolith-server
RUN go build ./cmd/generate-keys RUN go build ./cmd/generate-keys
RUN go build ./cmd/generate-config RUN go build ./cmd/generate-config
RUN go build ./cmd/create-account
RUN ./generate-config --ci > dendrite.yaml RUN ./generate-config --ci > dendrite.yaml
RUN ./generate-keys --private-key matrix_key.pem --tls-cert server.crt --tls-key server.key RUN ./generate-keys --private-key matrix_key.pem --tls-cert server.crt --tls-key server.key
@ -458,6 +461,46 @@ func loadAndRunTests(dockerClient *client.Client, volumeName, v string, branchTo
if err = runTests(csAPIURL, v); err != nil { if err = runTests(csAPIURL, v); err != nil {
return fmt.Errorf("failed to run tests on version %s: %s", v, err) return fmt.Errorf("failed to run tests on version %s: %s", v, err)
} }
err = testCreateAccount(dockerClient, v, containerID)
if err != nil {
return err
}
return nil
}
// test that create-account is working
func testCreateAccount(dockerClient *client.Client, v string, containerID string) error {
createUser := strings.ToLower("createaccountuser-" + v)
log.Printf("%s: Creating account %s with create-account\n", v, createUser)
respID, err := dockerClient.ContainerExecCreate(context.Background(), containerID, types.ExecConfig{
AttachStderr: true,
AttachStdout: true,
Cmd: []string{
"/build/create-account",
"-username", createUser,
"-password", "someRandomPassword",
},
})
if err != nil {
return fmt.Errorf("failed to ContainerExecCreate: %w", err)
}
response, err := dockerClient.ContainerExecAttach(context.Background(), respID.ID, types.ExecStartCheck{})
if err != nil {
return fmt.Errorf("failed to attach to container: %w", err)
}
defer response.Close()
data, err := ioutil.ReadAll(response.Reader)
if err != nil {
return err
}
if !bytes.Contains(data, []byte("AccessToken")) {
return fmt.Errorf("failed to create-account: %s", string(data))
}
return nil return nil
} }

View file

@ -9,6 +9,28 @@ permalink: /development/contributing
Everyone is welcome to contribute to Dendrite! We aim to make it as easy as Everyone is welcome to contribute to Dendrite! We aim to make it as easy as
possible to get started. possible to get started.
## Contribution types
We are a small team maintaining a large project. As a result, we cannot merge every feature, even if it
is bug-free and useful, because we then commit to maintaining it indefinitely. We will always accept:
- bug fixes
- security fixes (please responsibly disclose via security@matrix.org *before* creating pull requests)
We will accept the following with caveats:
- documentation fixes, provided they do not add additional instructions which can end up going out-of-date,
e.g example configs, shell commands.
- performance fixes, provided they do not add significantly more maintenance burden.
- additional functionality on existing features, provided the functionality is small and maintainable.
- additional functionality that, in its absence, would impact the ecosystem e.g spam and abuse mitigations
- test-only changes, provided they help improve coverage or test tricky code.
The following items are at risk of not being accepted:
- Configuration or CLI changes, particularly ones which increase the overall configuration surface.
The following items are unlikely to be accepted into a main Dendrite release for now:
- New MSC implementations.
- New features which are not in the specification.
## Sign off ## Sign off
We require that everyone who contributes to the project signs off their contributions We require that everyone who contributes to the project signs off their contributions
@ -75,7 +97,20 @@ comment. Please avoid doing this if you can.
We also have unit tests which we run via: We also have unit tests which we run via:
```bash ```bash
go test --race ./... DENDRITE_TEST_SKIP_NODB=1 go test --race ./...
```
This only runs SQLite database tests. If you wish to execute Postgres tests as well, you'll either need to
have Postgres installed locally (`createdb` will be used) or have a remote/containerized Postgres instance
available.
To configure the connection to a remote Postgres, you can use the following enviroment variables:
```bash
POSTGRES_USER=postgres
POSTGERS_PASSWORD=yourPostgresPassword
POSTGRES_HOST=localhost
POSTGRES_DB=postgres # the superuser database to use
``` ```
In general, we like submissions that come with tests. Anything that proves that the In general, we like submissions that come with tests. Anything that proves that the

View file

@ -231,9 +231,9 @@ GEM
jekyll-seo-tag (~> 2.1) jekyll-seo-tag (~> 2.1)
minitest (5.15.0) minitest (5.15.0)
multipart-post (2.1.1) multipart-post (2.1.1)
nokogiri (1.13.9-arm64-darwin) nokogiri (1.13.10-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.13.9-x86_64-linux) nokogiri (1.13.10-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
octokit (4.22.0) octokit (4.22.0)
faraday (>= 0.9) faraday (>= 0.9)
@ -241,7 +241,7 @@ GEM
pathutil (0.16.2) pathutil (0.16.2)
forwardable-extended (~> 2.6) forwardable-extended (~> 2.6)
public_suffix (4.0.7) public_suffix (4.0.7)
racc (1.6.0) racc (1.6.1)
rb-fsevent (0.11.1) rb-fsevent (0.11.1)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)

View file

@ -232,7 +232,7 @@ func (s *OutputRoomEventConsumer) processMessage(ore api.OutputNewRoomEvent, rew
} }
func (s *OutputRoomEventConsumer) sendPresence(roomID string, addedJoined []types.JoinedHost) { func (s *OutputRoomEventConsumer) sendPresence(roomID string, addedJoined []types.JoinedHost) {
joined := make([]gomatrixserverlib.ServerName, len(addedJoined)) joined := make([]gomatrixserverlib.ServerName, 0, len(addedJoined))
for _, added := range addedJoined { for _, added := range addedJoined {
joined = append(joined, added.ServerName) joined = append(joined, added.ServerName)
} }

View file

@ -43,8 +43,8 @@ import (
// AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions // AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions
// on the given input API. // on the given input API.
func AddInternalRoutes(router *mux.Router, intAPI api.FederationInternalAPI) { func AddInternalRoutes(router *mux.Router, intAPI api.FederationInternalAPI, enableMetrics bool) {
inthttp.AddRoutes(intAPI, router) inthttp.AddRoutes(intAPI, router, enableMetrics)
} }
// AddPublicRoutes sets up and registers HTTP handlers on the base API muxes for the FederationAPI component. // AddPublicRoutes sets up and registers HTTP handlers on the base API muxes for the FederationAPI component.

View file

@ -17,41 +17,41 @@ import (
// AddRoutes adds the FederationInternalAPI handlers to the http.ServeMux. // AddRoutes adds the FederationInternalAPI handlers to the http.ServeMux.
// nolint:gocyclo // nolint:gocyclo
func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) { func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router, enableMetrics bool) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIQueryJoinedHostServerNamesInRoomPath, FederationAPIQueryJoinedHostServerNamesInRoomPath,
httputil.MakeInternalRPCAPI("FederationAPIQueryJoinedHostServerNamesInRoom", intAPI.QueryJoinedHostServerNamesInRoom), httputil.MakeInternalRPCAPI("FederationAPIQueryJoinedHostServerNamesInRoom", enableMetrics, intAPI.QueryJoinedHostServerNamesInRoom),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIPerformInviteRequestPath, FederationAPIPerformInviteRequestPath,
httputil.MakeInternalRPCAPI("FederationAPIPerformInvite", intAPI.PerformInvite), httputil.MakeInternalRPCAPI("FederationAPIPerformInvite", enableMetrics, intAPI.PerformInvite),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIPerformLeaveRequestPath, FederationAPIPerformLeaveRequestPath,
httputil.MakeInternalRPCAPI("FederationAPIPerformLeave", intAPI.PerformLeave), httputil.MakeInternalRPCAPI("FederationAPIPerformLeave", enableMetrics, intAPI.PerformLeave),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIPerformDirectoryLookupRequestPath, FederationAPIPerformDirectoryLookupRequestPath,
httputil.MakeInternalRPCAPI("FederationAPIPerformDirectoryLookupRequest", intAPI.PerformDirectoryLookup), httputil.MakeInternalRPCAPI("FederationAPIPerformDirectoryLookupRequest", enableMetrics, intAPI.PerformDirectoryLookup),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIPerformBroadcastEDUPath, FederationAPIPerformBroadcastEDUPath,
httputil.MakeInternalRPCAPI("FederationAPIPerformBroadcastEDU", intAPI.PerformBroadcastEDU), httputil.MakeInternalRPCAPI("FederationAPIPerformBroadcastEDU", enableMetrics, intAPI.PerformBroadcastEDU),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIPerformWakeupServers, FederationAPIPerformWakeupServers,
httputil.MakeInternalRPCAPI("FederationAPIPerformWakeupServers", intAPI.PerformWakeupServers), httputil.MakeInternalRPCAPI("FederationAPIPerformWakeupServers", enableMetrics, intAPI.PerformWakeupServers),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIPerformJoinRequestPath, FederationAPIPerformJoinRequestPath,
httputil.MakeInternalRPCAPI( httputil.MakeInternalRPCAPI(
"FederationAPIPerformJoinRequest", "FederationAPIPerformJoinRequest", enableMetrics,
func(ctx context.Context, req *api.PerformJoinRequest, res *api.PerformJoinResponse) error { func(ctx context.Context, req *api.PerformJoinRequest, res *api.PerformJoinResponse) error {
intAPI.PerformJoin(ctx, req, res) intAPI.PerformJoin(ctx, req, res)
return nil return nil
@ -62,7 +62,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIGetUserDevicesPath, FederationAPIGetUserDevicesPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPIGetUserDevices", "FederationAPIGetUserDevices", enableMetrics,
func(ctx context.Context, req *getUserDevices) (*gomatrixserverlib.RespUserDevices, error) { func(ctx context.Context, req *getUserDevices) (*gomatrixserverlib.RespUserDevices, error) {
res, err := intAPI.GetUserDevices(ctx, req.Origin, req.S, req.UserID) res, err := intAPI.GetUserDevices(ctx, req.Origin, req.S, req.UserID)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -73,7 +73,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIClaimKeysPath, FederationAPIClaimKeysPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPIClaimKeys", "FederationAPIClaimKeys", enableMetrics,
func(ctx context.Context, req *claimKeys) (*gomatrixserverlib.RespClaimKeys, error) { func(ctx context.Context, req *claimKeys) (*gomatrixserverlib.RespClaimKeys, error) {
res, err := intAPI.ClaimKeys(ctx, req.Origin, req.S, req.OneTimeKeys) res, err := intAPI.ClaimKeys(ctx, req.Origin, req.S, req.OneTimeKeys)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -84,7 +84,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIQueryKeysPath, FederationAPIQueryKeysPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPIQueryKeys", "FederationAPIQueryKeys", enableMetrics,
func(ctx context.Context, req *queryKeys) (*gomatrixserverlib.RespQueryKeys, error) { func(ctx context.Context, req *queryKeys) (*gomatrixserverlib.RespQueryKeys, error) {
res, err := intAPI.QueryKeys(ctx, req.Origin, req.S, req.Keys) res, err := intAPI.QueryKeys(ctx, req.Origin, req.S, req.Keys)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -95,7 +95,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIBackfillPath, FederationAPIBackfillPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPIBackfill", "FederationAPIBackfill", enableMetrics,
func(ctx context.Context, req *backfill) (*gomatrixserverlib.Transaction, error) { func(ctx context.Context, req *backfill) (*gomatrixserverlib.Transaction, error) {
res, err := intAPI.Backfill(ctx, req.Origin, req.S, req.RoomID, req.Limit, req.EventIDs) res, err := intAPI.Backfill(ctx, req.Origin, req.S, req.RoomID, req.Limit, req.EventIDs)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -106,7 +106,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPILookupStatePath, FederationAPILookupStatePath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPILookupState", "FederationAPILookupState", enableMetrics,
func(ctx context.Context, req *lookupState) (*gomatrixserverlib.RespState, error) { func(ctx context.Context, req *lookupState) (*gomatrixserverlib.RespState, error) {
res, err := intAPI.LookupState(ctx, req.Origin, req.S, req.RoomID, req.EventID, req.RoomVersion) res, err := intAPI.LookupState(ctx, req.Origin, req.S, req.RoomID, req.EventID, req.RoomVersion)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -117,7 +117,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPILookupStateIDsPath, FederationAPILookupStateIDsPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPILookupStateIDs", "FederationAPILookupStateIDs", enableMetrics,
func(ctx context.Context, req *lookupStateIDs) (*gomatrixserverlib.RespStateIDs, error) { func(ctx context.Context, req *lookupStateIDs) (*gomatrixserverlib.RespStateIDs, error) {
res, err := intAPI.LookupStateIDs(ctx, req.Origin, req.S, req.RoomID, req.EventID) res, err := intAPI.LookupStateIDs(ctx, req.Origin, req.S, req.RoomID, req.EventID)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -128,7 +128,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPILookupMissingEventsPath, FederationAPILookupMissingEventsPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPILookupMissingEvents", "FederationAPILookupMissingEvents", enableMetrics,
func(ctx context.Context, req *lookupMissingEvents) (*gomatrixserverlib.RespMissingEvents, error) { func(ctx context.Context, req *lookupMissingEvents) (*gomatrixserverlib.RespMissingEvents, error) {
res, err := intAPI.LookupMissingEvents(ctx, req.Origin, req.S, req.RoomID, req.Missing, req.RoomVersion) res, err := intAPI.LookupMissingEvents(ctx, req.Origin, req.S, req.RoomID, req.Missing, req.RoomVersion)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -139,7 +139,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIGetEventPath, FederationAPIGetEventPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPIGetEvent", "FederationAPIGetEvent", enableMetrics,
func(ctx context.Context, req *getEvent) (*gomatrixserverlib.Transaction, error) { func(ctx context.Context, req *getEvent) (*gomatrixserverlib.Transaction, error) {
res, err := intAPI.GetEvent(ctx, req.Origin, req.S, req.EventID) res, err := intAPI.GetEvent(ctx, req.Origin, req.S, req.EventID)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -150,7 +150,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIGetEventAuthPath, FederationAPIGetEventAuthPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPIGetEventAuth", "FederationAPIGetEventAuth", enableMetrics,
func(ctx context.Context, req *getEventAuth) (*gomatrixserverlib.RespEventAuth, error) { func(ctx context.Context, req *getEventAuth) (*gomatrixserverlib.RespEventAuth, error) {
res, err := intAPI.GetEventAuth(ctx, req.Origin, req.S, req.RoomVersion, req.RoomID, req.EventID) res, err := intAPI.GetEventAuth(ctx, req.Origin, req.S, req.RoomVersion, req.RoomID, req.EventID)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -160,13 +160,13 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIQueryServerKeysPath, FederationAPIQueryServerKeysPath,
httputil.MakeInternalRPCAPI("FederationAPIQueryServerKeys", intAPI.QueryServerKeys), httputil.MakeInternalRPCAPI("FederationAPIQueryServerKeys", enableMetrics, intAPI.QueryServerKeys),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPILookupServerKeysPath, FederationAPILookupServerKeysPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPILookupServerKeys", "FederationAPILookupServerKeys", enableMetrics,
func(ctx context.Context, req *lookupServerKeys) (*[]gomatrixserverlib.ServerKeys, error) { func(ctx context.Context, req *lookupServerKeys) (*[]gomatrixserverlib.ServerKeys, error) {
res, err := intAPI.LookupServerKeys(ctx, req.S, req.KeyRequests) res, err := intAPI.LookupServerKeys(ctx, req.S, req.KeyRequests)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -177,7 +177,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPIEventRelationshipsPath, FederationAPIEventRelationshipsPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPIMSC2836EventRelationships", "FederationAPIMSC2836EventRelationships", enableMetrics,
func(ctx context.Context, req *eventRelationships) (*gomatrixserverlib.MSC2836EventRelationshipsResponse, error) { func(ctx context.Context, req *eventRelationships) (*gomatrixserverlib.MSC2836EventRelationshipsResponse, error) {
res, err := intAPI.MSC2836EventRelationships(ctx, req.Origin, req.S, req.Req, req.RoomVer) res, err := intAPI.MSC2836EventRelationships(ctx, req.Origin, req.S, req.Req, req.RoomVer)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -188,7 +188,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle( internalAPIMux.Handle(
FederationAPISpacesSummaryPath, FederationAPISpacesSummaryPath,
httputil.MakeInternalProxyAPI( httputil.MakeInternalProxyAPI(
"FederationAPIMSC2946SpacesSummary", "FederationAPIMSC2946SpacesSummary", enableMetrics,
func(ctx context.Context, req *spacesReq) (*gomatrixserverlib.MSC2946SpacesResponse, error) { func(ctx context.Context, req *spacesReq) (*gomatrixserverlib.MSC2946SpacesResponse, error) {
res, err := intAPI.MSC2946Spaces(ctx, req.Origin, req.S, req.RoomID, req.SuggestedOnly) res, err := intAPI.MSC2946Spaces(ctx, req.Origin, req.S, req.RoomID, req.SuggestedOnly)
return &res, federationClientError(err) return &res, federationClientError(err)
@ -198,7 +198,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
// TODO: Look at this shape // TODO: Look at this shape
internalAPIMux.Handle(FederationAPIQueryPublicKeyPath, internalAPIMux.Handle(FederationAPIQueryPublicKeyPath,
httputil.MakeInternalAPI("FederationAPIQueryPublicKeys", func(req *http.Request) util.JSONResponse { httputil.MakeInternalAPI("FederationAPIQueryPublicKeys", enableMetrics, func(req *http.Request) util.JSONResponse {
request := api.QueryPublicKeysRequest{} request := api.QueryPublicKeysRequest{}
response := api.QueryPublicKeysResponse{} response := api.QueryPublicKeysResponse{}
if err := json.NewDecoder(req.Body).Decode(&request); err != nil { if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
@ -215,7 +215,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
// TODO: Look at this shape // TODO: Look at this shape
internalAPIMux.Handle(FederationAPIInputPublicKeyPath, internalAPIMux.Handle(FederationAPIInputPublicKeyPath,
httputil.MakeInternalAPI("FederationAPIInputPublicKeys", func(req *http.Request) util.JSONResponse { httputil.MakeInternalAPI("FederationAPIInputPublicKeys", enableMetrics, func(req *http.Request) util.JSONResponse {
request := api.InputPublicKeysRequest{} request := api.InputPublicKeysRequest{}
response := api.InputPublicKeysResponse{} response := api.InputPublicKeysResponse{}
if err := json.NewDecoder(req.Body).Decode(&request); err != nil { if err := json.NewDecoder(req.Body).Decode(&request); err != nil {

4
go.mod
View file

@ -22,11 +22,11 @@ require (
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
github.com/matrix-org/gomatrixserverlib v0.0.0-20221118122129-9b9340bf29d7 github.com/matrix-org/gomatrixserverlib v0.0.0-20221129095800-8835f6db16b8
github.com/matrix-org/pinecone v0.0.0-20221118192051-fef26631b847 github.com/matrix-org/pinecone v0.0.0-20221118192051-fef26631b847
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
github.com/mattn/go-sqlite3 v1.14.15 github.com/mattn/go-sqlite3 v1.14.15
github.com/nats-io/nats-server/v2 v2.9.6 github.com/nats-io/nats-server/v2 v2.9.8
github.com/nats-io/nats.go v1.20.0 github.com/nats-io/nats.go v1.20.0
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9 github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646

8
go.sum
View file

@ -348,8 +348,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo= github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo=
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U=
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
github.com/matrix-org/gomatrixserverlib v0.0.0-20221118122129-9b9340bf29d7 h1:S2TNN7C00CZlE1Af31LzxkOsAEkFt0RYZ7/3VdR1D5U= github.com/matrix-org/gomatrixserverlib v0.0.0-20221129095800-8835f6db16b8 h1:jVvlCGs6OosCdvw9MkfiVnTVnIt7vKMHg/F6th9BtSo=
github.com/matrix-org/gomatrixserverlib v0.0.0-20221118122129-9b9340bf29d7/go.mod h1:Mtifyr8q8htcBeugvlDnkBcNUy5LO8OzUoplAf1+mb4= github.com/matrix-org/gomatrixserverlib v0.0.0-20221129095800-8835f6db16b8/go.mod h1:Mtifyr8q8htcBeugvlDnkBcNUy5LO8OzUoplAf1+mb4=
github.com/matrix-org/pinecone v0.0.0-20221118192051-fef26631b847 h1:auIBCi7gfZuvztD0aPr1G/J5Ya5vWr79M/+TJqwD/JM= github.com/matrix-org/pinecone v0.0.0-20221118192051-fef26631b847 h1:auIBCi7gfZuvztD0aPr1G/J5Ya5vWr79M/+TJqwD/JM=
github.com/matrix-org/pinecone v0.0.0-20221118192051-fef26631b847/go.mod h1:F3GHppRuHCTDeoOmmgjZMeJdbql91+RSGGsATWfC7oc= github.com/matrix-org/pinecone v0.0.0-20221118192051-fef26631b847/go.mod h1:F3GHppRuHCTDeoOmmgjZMeJdbql91+RSGGsATWfC7oc=
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 h1:eCEHXWDv9Rm335MSuB49mFUK44bwZPFSDde3ORE3syk= github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 h1:eCEHXWDv9Rm335MSuB49mFUK44bwZPFSDde3ORE3syk=
@ -385,8 +385,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI=
github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
github.com/nats-io/nats-server/v2 v2.9.6 h1:RTtK+rv/4CcliOuqGsy58g7MuWkBaWmF5TUNwuUo9Uw= github.com/nats-io/nats-server/v2 v2.9.8 h1:jgxZsv+A3Reb3MgwxaINcNq/za8xZInKhDg9Q0cGN1o=
github.com/nats-io/nats-server/v2 v2.9.6/go.mod h1:AB6hAnGZDlYfqb7CTAm66ZKMZy9DpfierY1/PbpvI2g= github.com/nats-io/nats-server/v2 v2.9.8/go.mod h1:AB6hAnGZDlYfqb7CTAm66ZKMZy9DpfierY1/PbpvI2g=
github.com/nats-io/nats.go v1.20.0 h1:T8JJnQfVSdh1CzGiwAOv5hEobYCBho/0EupGznYw0oM= github.com/nats-io/nats.go v1.20.0 h1:T8JJnQfVSdh1CzGiwAOv5hEobYCBho/0EupGznYw0oM=
github.com/nats-io/nats.go v1.20.0/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA= github.com/nats-io/nats.go v1.20.0/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA=
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=

View file

@ -24,16 +24,17 @@ import (
"strings" "strings"
"github.com/getsentry/sentry-go" "github.com/getsentry/sentry-go"
"github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/util" "github.com/matrix-org/util"
opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/ext"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
userapi "github.com/matrix-org/dendrite/userapi/api"
) )
// BasicAuth is used for authorization on /metrics handlers // BasicAuth is used for authorization on /metrics handlers
@ -227,7 +228,7 @@ func MakeHTMLAPI(metricsName string, f func(http.ResponseWriter, *http.Request)
// This is used for APIs that are internal to dendrite. // This is used for APIs that are internal to dendrite.
// If we are passed a tracing context in the request headers then we use that // If we are passed a tracing context in the request headers then we use that
// as the parent of any tracing spans we create. // as the parent of any tracing spans we create.
func MakeInternalAPI(metricsName string, f func(*http.Request) util.JSONResponse) http.Handler { func MakeInternalAPI(metricsName string, enableMetrics bool, f func(*http.Request) util.JSONResponse) http.Handler {
h := util.MakeJSONAPI(util.NewJSONRequestHandler(f)) h := util.MakeJSONAPI(util.NewJSONRequestHandler(f))
withSpan := func(w http.ResponseWriter, req *http.Request) { withSpan := func(w http.ResponseWriter, req *http.Request) {
carrier := opentracing.HTTPHeadersCarrier(req.Header) carrier := opentracing.HTTPHeadersCarrier(req.Header)
@ -246,6 +247,10 @@ func MakeInternalAPI(metricsName string, f func(*http.Request) util.JSONResponse
h.ServeHTTP(w, req) h.ServeHTTP(w, req)
} }
if !enableMetrics {
return http.HandlerFunc(withSpan)
}
return promhttp.InstrumentHandlerCounter( return promhttp.InstrumentHandlerCounter(
promauto.NewCounterVec( promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{

View file

@ -22,7 +22,7 @@ import (
"reflect" "reflect"
"github.com/matrix-org/util" "github.com/matrix-org/util"
opentracing "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go"
) )
type InternalAPIError struct { type InternalAPIError struct {
@ -34,8 +34,8 @@ func (e InternalAPIError) Error() string {
return fmt.Sprintf("internal API returned %q error: %s", e.Type, e.Message) return fmt.Sprintf("internal API returned %q error: %s", e.Type, e.Message)
} }
func MakeInternalRPCAPI[reqtype, restype any](metricsName string, f func(context.Context, *reqtype, *restype) error) http.Handler { func MakeInternalRPCAPI[reqtype, restype any](metricsName string, enableMetrics bool, f func(context.Context, *reqtype, *restype) error) http.Handler {
return MakeInternalAPI(metricsName, func(req *http.Request) util.JSONResponse { return MakeInternalAPI(metricsName, enableMetrics, func(req *http.Request) util.JSONResponse {
var request reqtype var request reqtype
var response restype var response restype
if err := json.NewDecoder(req.Body).Decode(&request); err != nil { if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
@ -57,8 +57,8 @@ func MakeInternalRPCAPI[reqtype, restype any](metricsName string, f func(context
}) })
} }
func MakeInternalProxyAPI[reqtype, restype any](metricsName string, f func(context.Context, *reqtype) (*restype, error)) http.Handler { func MakeInternalProxyAPI[reqtype, restype any](metricsName string, enableMetrics bool, f func(context.Context, *reqtype) (*restype, error)) http.Handler {
return MakeInternalAPI(metricsName, func(req *http.Request) util.JSONResponse { return MakeInternalAPI(metricsName, enableMetrics, func(req *http.Request) util.JSONResponse {
var request reqtype var request reqtype
if err := json.NewDecoder(req.Body).Decode(&request); err != nil { if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error()) return util.MessageResponse(http.StatusBadRequest, err.Error())

View file

@ -33,6 +33,11 @@ import (
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
) )
// logrus is using a global variable when we're using `logrus.AddHook`
// this unfortunately results in us adding the same hook multiple times.
// This map ensures we only ever add one level hook.
var stdLevelLogAdded = make(map[logrus.Level]bool)
type utcFormatter struct { type utcFormatter struct {
logrus.Formatter logrus.Formatter
} }

View file

@ -22,16 +22,16 @@ import (
"log/syslog" "log/syslog"
"github.com/MFAshby/stdemuxerhook" "github.com/MFAshby/stdemuxerhook"
"github.com/matrix-org/dendrite/setup/config"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
lSyslog "github.com/sirupsen/logrus/hooks/syslog" lSyslog "github.com/sirupsen/logrus/hooks/syslog"
"github.com/matrix-org/dendrite/setup/config"
) )
// SetupHookLogging configures the logging hooks defined in the configuration. // SetupHookLogging configures the logging hooks defined in the configuration.
// If something fails here it means that the logging was improperly configured, // If something fails here it means that the logging was improperly configured,
// so we just exit with the error // so we just exit with the error
func SetupHookLogging(hooks []config.LogrusHook, componentName string) { func SetupHookLogging(hooks []config.LogrusHook, componentName string) {
stdLogAdded := false
for _, hook := range hooks { for _, hook := range hooks {
// Check we received a proper logging level // Check we received a proper logging level
level, err := logrus.ParseLevel(hook.Level) level, err := logrus.ParseLevel(hook.Level)
@ -54,14 +54,11 @@ func SetupHookLogging(hooks []config.LogrusHook, componentName string) {
setupSyslogHook(hook, level, componentName) setupSyslogHook(hook, level, componentName)
case "std": case "std":
setupStdLogHook(level) setupStdLogHook(level)
stdLogAdded = true
default: default:
logrus.Fatalf("Unrecognised logging hook type: %s", hook.Type) logrus.Fatalf("Unrecognised logging hook type: %s", hook.Type)
} }
} }
if !stdLogAdded { setupStdLogHook(logrus.InfoLevel)
setupStdLogHook(logrus.InfoLevel)
}
// Hooks are now configured for stdout/err, so throw away the default logger output // Hooks are now configured for stdout/err, so throw away the default logger output
logrus.SetOutput(io.Discard) logrus.SetOutput(io.Discard)
} }
@ -88,7 +85,11 @@ func checkSyslogHookParams(params map[string]interface{}) {
} }
func setupStdLogHook(level logrus.Level) { func setupStdLogHook(level logrus.Level) {
if stdLevelLogAdded[level] {
return
}
logrus.AddHook(&logLevelHook{level, stdemuxerhook.New(logrus.StandardLogger())}) logrus.AddHook(&logLevelHook{level, stdemuxerhook.New(logrus.StandardLogger())})
stdLevelLogAdded[level] = true
} }
func setupSyslogHook(hook config.LogrusHook, level logrus.Level, componentName string) { func setupSyslogHook(hook config.LogrusHook, level logrus.Level, componentName string) {

View file

@ -9,6 +9,8 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/matrix-org/dendrite/internal"
"github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go"
) )
@ -50,8 +52,7 @@ func (h *httpClient) Notify(ctx context.Context, url string, req *NotifyRequest,
return err return err
} }
//nolint:errcheck defer internal.CloseAndLogIfError(ctx, hresp.Body, "failed to close response body")
defer hresp.Body.Close()
if hresp.StatusCode == http.StatusOK { if hresp.StatusCode == http.StatusOK {
return json.NewDecoder(hresp.Body).Decode(resp) return json.NewDecoder(hresp.Body).Decode(resp)

View file

@ -0,0 +1,54 @@
package pushgateway
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"reflect"
"testing"
)
func TestNotify(t *testing.T) {
wantResponse := NotifyResponse{
Rejected: []string{"testing"},
}
var i = 0
svr := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// /notify only accepts POST requests
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusNotImplemented)
return
}
if i != 0 { // error path
w.WriteHeader(http.StatusBadRequest)
return
}
// happy path
json.NewEncoder(w).Encode(wantResponse)
}))
defer svr.Close()
cl := NewHTTPClient(true)
gotResponse := NotifyResponse{}
// Test happy path
err := cl.Notify(context.Background(), svr.URL, &NotifyRequest{}, &gotResponse)
if err != nil {
t.Errorf("failed to notify client")
}
if !reflect.DeepEqual(gotResponse, wantResponse) {
t.Errorf("expected response %+v, got %+v", wantResponse, gotResponse)
}
// Test error path
i++
err = cl.Notify(context.Background(), svr.URL, &NotifyRequest{}, &gotResponse)
if err == nil {
t.Errorf("expected notifying the pushgateway to fail, but it succeeded")
}
}

View file

@ -145,6 +145,11 @@ func conditionMatches(cond *Condition, event *gomatrixserverlib.Event, ec Evalua
} }
func patternMatches(key, pattern string, event *gomatrixserverlib.Event) (bool, error) { func patternMatches(key, pattern string, event *gomatrixserverlib.Event) (bool, error) {
// It doesn't make sense for an empty pattern to match anything.
if pattern == "" {
return false, nil
}
re, err := globToRegexp(pattern) re, err := globToRegexp(pattern)
if err != nil { if err != nil {
return false, err return false, err
@ -154,12 +159,20 @@ func patternMatches(key, pattern string, event *gomatrixserverlib.Event) (bool,
if err = json.Unmarshal(event.JSON(), &eventMap); err != nil { if err = json.Unmarshal(event.JSON(), &eventMap); err != nil {
return false, fmt.Errorf("parsing event: %w", err) return false, fmt.Errorf("parsing event: %w", err)
} }
// From the spec:
// "If the property specified by key is completely absent from
// the event, or does not have a string value, then the condition
// will not match, even if pattern is *."
v, err := lookupMapPath(strings.Split(key, "."), eventMap) v, err := lookupMapPath(strings.Split(key, "."), eventMap)
if err != nil { if err != nil {
// An unknown path is a benign error that shouldn't stop rule // An unknown path is a benign error that shouldn't stop rule
// processing. It's just a non-match. // processing. It's just a non-match.
return false, nil return false, nil
} }
if _, ok := v.(string); !ok {
// A non-string never matches.
return false, nil
}
return re.MatchString(fmt.Sprint(v)), nil return re.MatchString(fmt.Sprint(v)), nil
} }

View file

@ -111,7 +111,10 @@ func TestConditionMatches(t *testing.T) {
{"empty", Condition{}, `{}`, false}, {"empty", Condition{}, `{}`, false},
{"empty", Condition{Kind: "unknownstring"}, `{}`, false}, {"empty", Condition{Kind: "unknownstring"}, `{}`, false},
{"eventMatch", Condition{Kind: EventMatchCondition, Key: "content"}, `{"content":{}}`, true}, // Neither of these should match because `content` is not a full string match,
// and `content.body` is not a string value.
{"eventMatch", Condition{Kind: EventMatchCondition, Key: "content"}, `{"content":{}}`, false},
{"eventBodyMatch", Condition{Kind: EventMatchCondition, Key: "content.body", Is: "3"}, `{"content":{"body": 3}}`, false},
{"displayNameNoMatch", Condition{Kind: ContainsDisplayNameCondition}, `{"content":{"body":"something without displayname"}}`, false}, {"displayNameNoMatch", Condition{Kind: ContainsDisplayNameCondition}, `{"content":{"body":"something without displayname"}}`, false},
{"displayNameMatch", Condition{Kind: ContainsDisplayNameCondition}, `{"content":{"body":"hello Dear User, how are you?"}}`, true}, {"displayNameMatch", Condition{Kind: ContainsDisplayNameCondition}, `{"content":{"body":"hello Dear User, how are you?"}}`, true},
@ -137,7 +140,7 @@ func TestConditionMatches(t *testing.T) {
t.Fatalf("conditionMatches failed: %v", err) t.Fatalf("conditionMatches failed: %v", err)
} }
if got != tst.Want { if got != tst.Want {
t.Errorf("conditionMatches: got %v, want %v", got, tst.Want) t.Errorf("conditionMatches: got %v, want %v on %s", got, tst.Want, tst.Name)
} }
}) })
} }
@ -161,9 +164,7 @@ func TestPatternMatches(t *testing.T) {
}{ }{
{"empty", "", "", `{}`, false}, {"empty", "", "", `{}`, false},
// Note that an empty pattern contains no wildcard characters, {"patternEmpty", "content", "", `{"content":{}}`, false},
// which implicitly means "*".
{"patternEmpty", "content", "", `{"content":{}}`, true},
{"literal", "content.creator", "acreator", `{"content":{"creator":"acreator"}}`, true}, {"literal", "content.creator", "acreator", `{"content":{"creator":"acreator"}}`, true},
{"substring", "content.creator", "reat", `{"content":{"creator":"acreator"}}`, true}, {"substring", "content.creator", "reat", `{"content":{"creator":"acreator"}}`, true},
@ -178,7 +179,7 @@ func TestPatternMatches(t *testing.T) {
t.Fatalf("patternMatches failed: %v", err) t.Fatalf("patternMatches failed: %v", err)
} }
if got != tst.Want { if got != tst.Want {
t.Errorf("patternMatches: got %v, want %v", got, tst.Want) t.Errorf("patternMatches: got %v, want %v on %s", got, tst.Want, tst.Name)
} }
}) })
} }

View file

@ -11,22 +11,27 @@ import (
// kind and a tweaks map. Returns a nil map if it would have been // kind and a tweaks map. Returns a nil map if it would have been
// empty. // empty.
func ActionsToTweaks(as []*Action) (ActionKind, map[string]interface{}, error) { func ActionsToTweaks(as []*Action) (ActionKind, map[string]interface{}, error) {
kind := UnknownAction var kind ActionKind
tweaks := map[string]interface{}{} var tweaks map[string]interface{}
for _, a := range as { for _, a := range as {
if a.Kind == SetTweakAction { switch a.Kind {
tweaks[string(a.Tweak)] = a.Value case DontNotifyAction:
continue // Don't bother processing any further
} return DontNotifyAction, nil, nil
if kind != UnknownAction {
return UnknownAction, nil, fmt.Errorf("got multiple primary actions: already had %q, got %s", kind, a.Kind)
}
kind = a.Kind
}
if len(tweaks) == 0 { case SetTweakAction:
tweaks = nil if tweaks == nil {
tweaks = map[string]interface{}{}
}
tweaks[string(a.Tweak)] = a.Value
default:
if kind != UnknownAction {
return UnknownAction, nil, fmt.Errorf("got multiple primary actions: already had %q, got %s", kind, a.Kind)
}
kind = a.Kind
}
} }
return kind, tweaks, nil return kind, tweaks, nil

View file

@ -17,6 +17,7 @@ func TestActionsToTweaks(t *testing.T) {
{"empty", nil, UnknownAction, nil}, {"empty", nil, UnknownAction, nil},
{"zero", []*Action{{}}, UnknownAction, nil}, {"zero", []*Action{{}}, UnknownAction, nil},
{"onlyPrimary", []*Action{{Kind: NotifyAction}}, NotifyAction, nil}, {"onlyPrimary", []*Action{{Kind: NotifyAction}}, NotifyAction, nil},
{"onlyPrimaryDontNotify", []*Action{{Kind: DontNotifyAction}}, DontNotifyAction, nil},
{"onlyTweak", []*Action{{Kind: SetTweakAction, Tweak: HighlightTweak}}, UnknownAction, map[string]interface{}{"highlight": nil}}, {"onlyTweak", []*Action{{Kind: SetTweakAction, Tweak: HighlightTweak}}, UnknownAction, map[string]interface{}{"highlight": nil}},
{"onlyTweakWithValue", []*Action{{Kind: SetTweakAction, Tweak: SoundTweak, Value: "default"}}, UnknownAction, map[string]interface{}{"sound": "default"}}, {"onlyTweakWithValue", []*Action{{Kind: SetTweakAction, Tweak: SoundTweak, Value: "default"}}, UnknownAction, map[string]interface{}{"sound": "default"}},
{ {

View file

@ -17,7 +17,7 @@ var build string
const ( const (
VersionMajor = 0 VersionMajor = 0
VersionMinor = 10 VersionMinor = 10
VersionPatch = 7 VersionPatch = 8
VersionTag = "" // example: "rc1" VersionTag = "" // example: "rc1"
) )

View file

@ -21,59 +21,59 @@ import (
"github.com/matrix-org/dendrite/keyserver/api" "github.com/matrix-org/dendrite/keyserver/api"
) )
func AddRoutes(internalAPIMux *mux.Router, s api.KeyInternalAPI) { func AddRoutes(internalAPIMux *mux.Router, s api.KeyInternalAPI, enableMetrics bool) {
internalAPIMux.Handle( internalAPIMux.Handle(
PerformClaimKeysPath, PerformClaimKeysPath,
httputil.MakeInternalRPCAPI("KeyserverPerformClaimKeys", s.PerformClaimKeys), httputil.MakeInternalRPCAPI("KeyserverPerformClaimKeys", enableMetrics, s.PerformClaimKeys),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformDeleteKeysPath, PerformDeleteKeysPath,
httputil.MakeInternalRPCAPI("KeyserverPerformDeleteKeys", s.PerformDeleteKeys), httputil.MakeInternalRPCAPI("KeyserverPerformDeleteKeys", enableMetrics, s.PerformDeleteKeys),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformUploadKeysPath, PerformUploadKeysPath,
httputil.MakeInternalRPCAPI("KeyserverPerformUploadKeys", s.PerformUploadKeys), httputil.MakeInternalRPCAPI("KeyserverPerformUploadKeys", enableMetrics, s.PerformUploadKeys),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformUploadDeviceKeysPath, PerformUploadDeviceKeysPath,
httputil.MakeInternalRPCAPI("KeyserverPerformUploadDeviceKeys", s.PerformUploadDeviceKeys), httputil.MakeInternalRPCAPI("KeyserverPerformUploadDeviceKeys", enableMetrics, s.PerformUploadDeviceKeys),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformUploadDeviceSignaturesPath, PerformUploadDeviceSignaturesPath,
httputil.MakeInternalRPCAPI("KeyserverPerformUploadDeviceSignatures", s.PerformUploadDeviceSignatures), httputil.MakeInternalRPCAPI("KeyserverPerformUploadDeviceSignatures", enableMetrics, s.PerformUploadDeviceSignatures),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryKeysPath, QueryKeysPath,
httputil.MakeInternalRPCAPI("KeyserverQueryKeys", s.QueryKeys), httputil.MakeInternalRPCAPI("KeyserverQueryKeys", enableMetrics, s.QueryKeys),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryOneTimeKeysPath, QueryOneTimeKeysPath,
httputil.MakeInternalRPCAPI("KeyserverQueryOneTimeKeys", s.QueryOneTimeKeys), httputil.MakeInternalRPCAPI("KeyserverQueryOneTimeKeys", enableMetrics, s.QueryOneTimeKeys),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryDeviceMessagesPath, QueryDeviceMessagesPath,
httputil.MakeInternalRPCAPI("KeyserverQueryDeviceMessages", s.QueryDeviceMessages), httputil.MakeInternalRPCAPI("KeyserverQueryDeviceMessages", enableMetrics, s.QueryDeviceMessages),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryKeyChangesPath, QueryKeyChangesPath,
httputil.MakeInternalRPCAPI("KeyserverQueryKeyChanges", s.QueryKeyChanges), httputil.MakeInternalRPCAPI("KeyserverQueryKeyChanges", enableMetrics, s.QueryKeyChanges),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QuerySignaturesPath, QuerySignaturesPath,
httputil.MakeInternalRPCAPI("KeyserverQuerySignatures", s.QuerySignatures), httputil.MakeInternalRPCAPI("KeyserverQuerySignatures", enableMetrics, s.QuerySignatures),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformMarkAsStalePath, PerformMarkAsStalePath,
httputil.MakeInternalRPCAPI("KeyserverMarkAsStale", s.PerformMarkAsStaleIfNeeded), httputil.MakeInternalRPCAPI("KeyserverMarkAsStale", enableMetrics, s.PerformMarkAsStaleIfNeeded),
) )
} }

View file

@ -33,8 +33,8 @@ import (
// AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions // AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions
// on the given input API. // on the given input API.
func AddInternalRoutes(router *mux.Router, intAPI api.KeyInternalAPI) { func AddInternalRoutes(router *mux.Router, intAPI api.KeyInternalAPI, enableMetrics bool) {
inthttp.AddRoutes(router, intAPI) inthttp.AddRoutes(router, intAPI, enableMetrics)
} }
// NewInternalAPI returns a concerete implementation of the internal API. Callers // NewInternalAPI returns a concerete implementation of the internal API. Callers

View file

@ -9,199 +9,199 @@ import (
// AddRoutes adds the RoomserverInternalAPI handlers to the http.ServeMux. // AddRoutes adds the RoomserverInternalAPI handlers to the http.ServeMux.
// nolint: gocyclo // nolint: gocyclo
func AddRoutes(r api.RoomserverInternalAPI, internalAPIMux *mux.Router) { func AddRoutes(r api.RoomserverInternalAPI, internalAPIMux *mux.Router, enableMetrics bool) {
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverInputRoomEventsPath, RoomserverInputRoomEventsPath,
httputil.MakeInternalRPCAPI("RoomserverInputRoomEvents", r.InputRoomEvents), httputil.MakeInternalRPCAPI("RoomserverInputRoomEvents", enableMetrics, r.InputRoomEvents),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformInvitePath, RoomserverPerformInvitePath,
httputil.MakeInternalRPCAPI("RoomserverPerformInvite", r.PerformInvite), httputil.MakeInternalRPCAPI("RoomserverPerformInvite", enableMetrics, r.PerformInvite),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformJoinPath, RoomserverPerformJoinPath,
httputil.MakeInternalRPCAPI("RoomserverPerformJoin", r.PerformJoin), httputil.MakeInternalRPCAPI("RoomserverPerformJoin", enableMetrics, r.PerformJoin),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformLeavePath, RoomserverPerformLeavePath,
httputil.MakeInternalRPCAPI("RoomserverPerformLeave", r.PerformLeave), httputil.MakeInternalRPCAPI("RoomserverPerformLeave", enableMetrics, r.PerformLeave),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformPeekPath, RoomserverPerformPeekPath,
httputil.MakeInternalRPCAPI("RoomserverPerformPeek", r.PerformPeek), httputil.MakeInternalRPCAPI("RoomserverPerformPeek", enableMetrics, r.PerformPeek),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformInboundPeekPath, RoomserverPerformInboundPeekPath,
httputil.MakeInternalRPCAPI("RoomserverPerformInboundPeek", r.PerformInboundPeek), httputil.MakeInternalRPCAPI("RoomserverPerformInboundPeek", enableMetrics, r.PerformInboundPeek),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformUnpeekPath, RoomserverPerformUnpeekPath,
httputil.MakeInternalRPCAPI("RoomserverPerformUnpeek", r.PerformUnpeek), httputil.MakeInternalRPCAPI("RoomserverPerformUnpeek", enableMetrics, r.PerformUnpeek),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformRoomUpgradePath, RoomserverPerformRoomUpgradePath,
httputil.MakeInternalRPCAPI("RoomserverPerformRoomUpgrade", r.PerformRoomUpgrade), httputil.MakeInternalRPCAPI("RoomserverPerformRoomUpgrade", enableMetrics, r.PerformRoomUpgrade),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformPublishPath, RoomserverPerformPublishPath,
httputil.MakeInternalRPCAPI("RoomserverPerformPublish", r.PerformPublish), httputil.MakeInternalRPCAPI("RoomserverPerformPublish", enableMetrics, r.PerformPublish),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformAdminEvacuateRoomPath, RoomserverPerformAdminEvacuateRoomPath,
httputil.MakeInternalRPCAPI("RoomserverPerformAdminEvacuateRoom", r.PerformAdminEvacuateRoom), httputil.MakeInternalRPCAPI("RoomserverPerformAdminEvacuateRoom", enableMetrics, r.PerformAdminEvacuateRoom),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformAdminEvacuateUserPath, RoomserverPerformAdminEvacuateUserPath,
httputil.MakeInternalRPCAPI("RoomserverPerformAdminEvacuateUser", r.PerformAdminEvacuateUser), httputil.MakeInternalRPCAPI("RoomserverPerformAdminEvacuateUser", enableMetrics, r.PerformAdminEvacuateUser),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformAdminDownloadStatePath, RoomserverPerformAdminDownloadStatePath,
httputil.MakeInternalRPCAPI("RoomserverPerformAdminDownloadState", r.PerformAdminDownloadState), httputil.MakeInternalRPCAPI("RoomserverPerformAdminDownloadState", enableMetrics, r.PerformAdminDownloadState),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryPublishedRoomsPath, RoomserverQueryPublishedRoomsPath,
httputil.MakeInternalRPCAPI("RoomserverQueryPublishedRooms", r.QueryPublishedRooms), httputil.MakeInternalRPCAPI("RoomserverQueryPublishedRooms", enableMetrics, r.QueryPublishedRooms),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryLatestEventsAndStatePath, RoomserverQueryLatestEventsAndStatePath,
httputil.MakeInternalRPCAPI("RoomserverQueryLatestEventsAndState", r.QueryLatestEventsAndState), httputil.MakeInternalRPCAPI("RoomserverQueryLatestEventsAndState", enableMetrics, r.QueryLatestEventsAndState),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryStateAfterEventsPath, RoomserverQueryStateAfterEventsPath,
httputil.MakeInternalRPCAPI("RoomserverQueryStateAfterEvents", r.QueryStateAfterEvents), httputil.MakeInternalRPCAPI("RoomserverQueryStateAfterEvents", enableMetrics, r.QueryStateAfterEvents),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryEventsByIDPath, RoomserverQueryEventsByIDPath,
httputil.MakeInternalRPCAPI("RoomserverQueryEventsByID", r.QueryEventsByID), httputil.MakeInternalRPCAPI("RoomserverQueryEventsByID", enableMetrics, r.QueryEventsByID),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryMembershipForUserPath, RoomserverQueryMembershipForUserPath,
httputil.MakeInternalRPCAPI("RoomserverQueryMembershipForUser", r.QueryMembershipForUser), httputil.MakeInternalRPCAPI("RoomserverQueryMembershipForUser", enableMetrics, r.QueryMembershipForUser),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryMembershipsForRoomPath, RoomserverQueryMembershipsForRoomPath,
httputil.MakeInternalRPCAPI("RoomserverQueryMembershipsForRoom", r.QueryMembershipsForRoom), httputil.MakeInternalRPCAPI("RoomserverQueryMembershipsForRoom", enableMetrics, r.QueryMembershipsForRoom),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryServerJoinedToRoomPath, RoomserverQueryServerJoinedToRoomPath,
httputil.MakeInternalRPCAPI("RoomserverQueryServerJoinedToRoom", r.QueryServerJoinedToRoom), httputil.MakeInternalRPCAPI("RoomserverQueryServerJoinedToRoom", enableMetrics, r.QueryServerJoinedToRoom),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryServerAllowedToSeeEventPath, RoomserverQueryServerAllowedToSeeEventPath,
httputil.MakeInternalRPCAPI("RoomserverQueryServerAllowedToSeeEvent", r.QueryServerAllowedToSeeEvent), httputil.MakeInternalRPCAPI("RoomserverQueryServerAllowedToSeeEvent", enableMetrics, r.QueryServerAllowedToSeeEvent),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryMissingEventsPath, RoomserverQueryMissingEventsPath,
httputil.MakeInternalRPCAPI("RoomserverQueryMissingEvents", r.QueryMissingEvents), httputil.MakeInternalRPCAPI("RoomserverQueryMissingEvents", enableMetrics, r.QueryMissingEvents),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryStateAndAuthChainPath, RoomserverQueryStateAndAuthChainPath,
httputil.MakeInternalRPCAPI("RoomserverQueryStateAndAuthChain", r.QueryStateAndAuthChain), httputil.MakeInternalRPCAPI("RoomserverQueryStateAndAuthChain", enableMetrics, r.QueryStateAndAuthChain),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformBackfillPath, RoomserverPerformBackfillPath,
httputil.MakeInternalRPCAPI("RoomserverPerformBackfill", r.PerformBackfill), httputil.MakeInternalRPCAPI("RoomserverPerformBackfill", enableMetrics, r.PerformBackfill),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverPerformForgetPath, RoomserverPerformForgetPath,
httputil.MakeInternalRPCAPI("RoomserverPerformForget", r.PerformForget), httputil.MakeInternalRPCAPI("RoomserverPerformForget", enableMetrics, r.PerformForget),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryRoomVersionCapabilitiesPath, RoomserverQueryRoomVersionCapabilitiesPath,
httputil.MakeInternalRPCAPI("RoomserverQueryRoomVersionCapabilities", r.QueryRoomVersionCapabilities), httputil.MakeInternalRPCAPI("RoomserverQueryRoomVersionCapabilities", enableMetrics, r.QueryRoomVersionCapabilities),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryRoomVersionForRoomPath, RoomserverQueryRoomVersionForRoomPath,
httputil.MakeInternalRPCAPI("RoomserverQueryRoomVersionForRoom", r.QueryRoomVersionForRoom), httputil.MakeInternalRPCAPI("RoomserverQueryRoomVersionForRoom", enableMetrics, r.QueryRoomVersionForRoom),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverSetRoomAliasPath, RoomserverSetRoomAliasPath,
httputil.MakeInternalRPCAPI("RoomserverSetRoomAlias", r.SetRoomAlias), httputil.MakeInternalRPCAPI("RoomserverSetRoomAlias", enableMetrics, r.SetRoomAlias),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverGetRoomIDForAliasPath, RoomserverGetRoomIDForAliasPath,
httputil.MakeInternalRPCAPI("RoomserverGetRoomIDForAlias", r.GetRoomIDForAlias), httputil.MakeInternalRPCAPI("RoomserverGetRoomIDForAlias", enableMetrics, r.GetRoomIDForAlias),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverGetAliasesForRoomIDPath, RoomserverGetAliasesForRoomIDPath,
httputil.MakeInternalRPCAPI("RoomserverGetAliasesForRoomID", r.GetAliasesForRoomID), httputil.MakeInternalRPCAPI("RoomserverGetAliasesForRoomID", enableMetrics, r.GetAliasesForRoomID),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverRemoveRoomAliasPath, RoomserverRemoveRoomAliasPath,
httputil.MakeInternalRPCAPI("RoomserverRemoveRoomAlias", r.RemoveRoomAlias), httputil.MakeInternalRPCAPI("RoomserverRemoveRoomAlias", enableMetrics, r.RemoveRoomAlias),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryCurrentStatePath, RoomserverQueryCurrentStatePath,
httputil.MakeInternalRPCAPI("RoomserverQueryCurrentState", r.QueryCurrentState), httputil.MakeInternalRPCAPI("RoomserverQueryCurrentState", enableMetrics, r.QueryCurrentState),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryRoomsForUserPath, RoomserverQueryRoomsForUserPath,
httputil.MakeInternalRPCAPI("RoomserverQueryRoomsForUser", r.QueryRoomsForUser), httputil.MakeInternalRPCAPI("RoomserverQueryRoomsForUser", enableMetrics, r.QueryRoomsForUser),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryBulkStateContentPath, RoomserverQueryBulkStateContentPath,
httputil.MakeInternalRPCAPI("RoomserverQueryBulkStateContent", r.QueryBulkStateContent), httputil.MakeInternalRPCAPI("RoomserverQueryBulkStateContent", enableMetrics, r.QueryBulkStateContent),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQuerySharedUsersPath, RoomserverQuerySharedUsersPath,
httputil.MakeInternalRPCAPI("RoomserverQuerySharedUsers", r.QuerySharedUsers), httputil.MakeInternalRPCAPI("RoomserverQuerySharedUsers", enableMetrics, r.QuerySharedUsers),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryKnownUsersPath, RoomserverQueryKnownUsersPath,
httputil.MakeInternalRPCAPI("RoomserverQueryKnownUsers", r.QueryKnownUsers), httputil.MakeInternalRPCAPI("RoomserverQueryKnownUsers", enableMetrics, r.QueryKnownUsers),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryServerBannedFromRoomPath, RoomserverQueryServerBannedFromRoomPath,
httputil.MakeInternalRPCAPI("RoomserverQueryServerBannedFromRoom", r.QueryServerBannedFromRoom), httputil.MakeInternalRPCAPI("RoomserverQueryServerBannedFromRoom", enableMetrics, r.QueryServerBannedFromRoom),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryAuthChainPath, RoomserverQueryAuthChainPath,
httputil.MakeInternalRPCAPI("RoomserverQueryAuthChain", r.QueryAuthChain), httputil.MakeInternalRPCAPI("RoomserverQueryAuthChain", enableMetrics, r.QueryAuthChain),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryRestrictedJoinAllowed, RoomserverQueryRestrictedJoinAllowed,
httputil.MakeInternalRPCAPI("RoomserverQueryRestrictedJoinAllowed", r.QueryRestrictedJoinAllowed), httputil.MakeInternalRPCAPI("RoomserverQueryRestrictedJoinAllowed", enableMetrics, r.QueryRestrictedJoinAllowed),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
RoomserverQueryMembershipAtEventPath, RoomserverQueryMembershipAtEventPath,
httputil.MakeInternalRPCAPI("RoomserverQueryMembershipAtEventPath", r.QueryMembershipAtEvent), httputil.MakeInternalRPCAPI("RoomserverQueryMembershipAtEventPath", enableMetrics, r.QueryMembershipAtEvent),
) )
internalAPIMux.Handle( internalAPIMux.Handle(

View file

@ -16,18 +16,19 @@ package roomserver
import ( import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/sirupsen/logrus"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/roomserver/internal" "github.com/matrix-org/dendrite/roomserver/internal"
"github.com/matrix-org/dendrite/roomserver/inthttp" "github.com/matrix-org/dendrite/roomserver/inthttp"
"github.com/matrix-org/dendrite/roomserver/storage" "github.com/matrix-org/dendrite/roomserver/storage"
"github.com/matrix-org/dendrite/setup/base" "github.com/matrix-org/dendrite/setup/base"
"github.com/sirupsen/logrus"
) )
// AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions // AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions
// on the given input API. // on the given input API.
func AddInternalRoutes(router *mux.Router, intAPI api.RoomserverInternalAPI) { func AddInternalRoutes(router *mux.Router, intAPI api.RoomserverInternalAPI, enableMetrics bool) {
inthttp.AddRoutes(intAPI, router) inthttp.AddRoutes(intAPI, router, enableMetrics)
} }
// NewInternalAPI returns a concerete implementation of the internal API. Callers // NewInternalAPI returns a concerete implementation of the internal API. Callers

View file

@ -413,6 +413,24 @@ func (b *BaseDendrite) configureHTTPErrors() {
b.PublicClientAPIMux.MethodNotAllowedHandler = http.HandlerFunc(clientNotFoundHandler) b.PublicClientAPIMux.MethodNotAllowedHandler = http.HandlerFunc(clientNotFoundHandler)
} }
func (b *BaseDendrite) ConfigureAdminEndpoints() {
b.DendriteAdminMux.HandleFunc("/monitor/up", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
})
b.DendriteAdminMux.HandleFunc("/monitor/health", func(w http.ResponseWriter, r *http.Request) {
if isDegraded, reasons := b.ProcessContext.IsDegraded(); isDegraded {
w.WriteHeader(503)
_ = json.NewEncoder(w).Encode(struct {
Warnings []string `json:"warnings"`
}{
Warnings: reasons,
})
return
}
w.WriteHeader(200)
})
}
// SetupAndServeHTTP sets up the HTTP server to serve endpoints registered on // SetupAndServeHTTP sets up the HTTP server to serve endpoints registered on
// ApiMux under /api/ and adds a prometheus handler under /metrics. // ApiMux under /api/ and adds a prometheus handler under /metrics.
func (b *BaseDendrite) SetupAndServeHTTP( func (b *BaseDendrite) SetupAndServeHTTP(
@ -463,21 +481,7 @@ func (b *BaseDendrite) SetupAndServeHTTP(
internalRouter.Handle("/metrics", httputil.WrapHandlerInBasicAuth(promhttp.Handler(), b.Cfg.Global.Metrics.BasicAuth)) internalRouter.Handle("/metrics", httputil.WrapHandlerInBasicAuth(promhttp.Handler(), b.Cfg.Global.Metrics.BasicAuth))
} }
b.DendriteAdminMux.HandleFunc("/monitor/up", func(w http.ResponseWriter, r *http.Request) { b.ConfigureAdminEndpoints()
w.WriteHeader(200)
})
b.DendriteAdminMux.HandleFunc("/monitor/health", func(w http.ResponseWriter, r *http.Request) {
if isDegraded, reasons := b.ProcessContext.IsDegraded(); isDegraded {
w.WriteHeader(503)
_ = json.NewEncoder(w).Encode(struct {
Warnings []string `json:"warnings"`
}{
Warnings: reasons,
})
return
}
w.WriteHeader(200)
})
var clientHandler http.Handler var clientHandler http.Handler
clientHandler = b.PublicClientAPIMux clientHandler = b.PublicClientAPIMux

View file

@ -17,7 +17,7 @@ type Global struct {
gomatrixserverlib.SigningIdentity `yaml:",inline"` gomatrixserverlib.SigningIdentity `yaml:",inline"`
// The secondary server names, used for virtual hosting. // The secondary server names, used for virtual hosting.
VirtualHosts []*VirtualHost `yaml:"virtual_hosts"` VirtualHosts []*VirtualHost `yaml:"-"`
// Path to the private key which will be used to sign requests and events. // Path to the private key which will be used to sign requests and events.
PrivateKeyPath Path `yaml:"private_key"` PrivateKeyPath Path `yaml:"private_key"`

View file

@ -78,7 +78,7 @@ func (s *PresenceConsumer) Start() error {
// Normal NATS subscription, used by Request/Reply // Normal NATS subscription, used by Request/Reply
_, err := s.nats.Subscribe(s.requestTopic, func(msg *nats.Msg) { _, err := s.nats.Subscribe(s.requestTopic, func(msg *nats.Msg) {
userID := msg.Header.Get(jetstream.UserID) userID := msg.Header.Get(jetstream.UserID)
presence, err := s.db.GetPresence(context.Background(), userID) presences, err := s.db.GetPresences(context.Background(), []string{userID})
m := &nats.Msg{ m := &nats.Msg{
Header: nats.Header{}, Header: nats.Header{},
} }
@ -89,10 +89,12 @@ func (s *PresenceConsumer) Start() error {
} }
return return
} }
if presence == nil {
presence = &types.PresenceInternal{ presence := &types.PresenceInternal{
UserID: userID, UserID: userID,
} }
if len(presences) > 0 {
presence = presences[0]
} }
deviceRes := api.QueryDevicesResponse{} deviceRes := api.QueryDevicesResponse{}

View file

@ -106,7 +106,7 @@ type DatabaseTransaction interface {
SelectMembershipForUser(ctx context.Context, roomID, userID string, pos int64) (membership string, topologicalPos int, err error) SelectMembershipForUser(ctx context.Context, roomID, userID string, pos int64) (membership string, topologicalPos int, err error)
// getUserUnreadNotificationCountsForRooms returns the unread notifications for the given rooms // getUserUnreadNotificationCountsForRooms returns the unread notifications for the given rooms
GetUserUnreadNotificationCountsForRooms(ctx context.Context, userID string, roomIDs map[string]string) (map[string]*eventutil.NotificationData, error) GetUserUnreadNotificationCountsForRooms(ctx context.Context, userID string, roomIDs map[string]string) (map[string]*eventutil.NotificationData, error)
GetPresence(ctx context.Context, userID string) (*types.PresenceInternal, error) GetPresences(ctx context.Context, userID []string) ([]*types.PresenceInternal, error)
PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error) PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error)
RelationsFor(ctx context.Context, roomID, eventID, relType, eventType string, from, to types.StreamPosition, backwards bool, limit int) (events []types.StreamEvent, prevBatch, nextBatch string, err error) RelationsFor(ctx context.Context, roomID, eventID, relType, eventType string, from, to types.StreamPosition, backwards bool, limit int) (events []types.StreamEvent, prevBatch, nextBatch string, err error)
} }
@ -186,7 +186,7 @@ type Database interface {
} }
type Presence interface { type Presence interface {
GetPresence(ctx context.Context, userID string) (*types.PresenceInternal, error) GetPresences(ctx context.Context, userIDs []string) ([]*types.PresenceInternal, error)
UpdatePresence(ctx context.Context, userID string, presence types.Presence, statusMsg *string, lastActiveTS gomatrixserverlib.Timestamp, fromSync bool) (types.StreamPosition, error) UpdatePresence(ctx context.Context, userID string, presence types.Presence, statusMsg *string, lastActiveTS gomatrixserverlib.Timestamp, fromSync bool) (types.StreamPosition, error)
} }

View file

@ -19,10 +19,12 @@ import (
"database/sql" "database/sql"
"time" "time"
"github.com/lib/pq"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/syncapi/types"
"github.com/matrix-org/gomatrixserverlib"
) )
const presenceSchema = ` const presenceSchema = `
@ -63,9 +65,9 @@ const upsertPresenceFromSyncSQL = "" +
" RETURNING id" " RETURNING id"
const selectPresenceForUserSQL = "" + const selectPresenceForUserSQL = "" +
"SELECT presence, status_msg, last_active_ts" + "SELECT user_id, presence, status_msg, last_active_ts" +
" FROM syncapi_presence" + " FROM syncapi_presence" +
" WHERE user_id = $1 LIMIT 1" " WHERE user_id = ANY($1)"
const selectMaxPresenceSQL = "" + const selectMaxPresenceSQL = "" +
"SELECT COALESCE(MAX(id), 0) FROM syncapi_presence" "SELECT COALESCE(MAX(id), 0) FROM syncapi_presence"
@ -119,20 +121,28 @@ func (p *presenceStatements) UpsertPresence(
return return
} }
// GetPresenceForUser returns the current presence of a user. // GetPresenceForUsers returns the current presence for a list of users.
func (p *presenceStatements) GetPresenceForUser( // If the user doesn't have a presence status yet, it is omitted from the response.
func (p *presenceStatements) GetPresenceForUsers(
ctx context.Context, txn *sql.Tx, ctx context.Context, txn *sql.Tx,
userID string, userIDs []string,
) (*types.PresenceInternal, error) { ) ([]*types.PresenceInternal, error) {
result := &types.PresenceInternal{ result := make([]*types.PresenceInternal, 0, len(userIDs))
UserID: userID,
}
stmt := sqlutil.TxStmt(txn, p.selectPresenceForUsersStmt) stmt := sqlutil.TxStmt(txn, p.selectPresenceForUsersStmt)
err := stmt.QueryRowContext(ctx, userID).Scan(&result.Presence, &result.ClientFields.StatusMsg, &result.LastActiveTS) rows, err := stmt.QueryContext(ctx, pq.Array(userIDs))
if err == sql.ErrNoRows { if err != nil {
return nil, nil return nil, err
}
defer internal.CloseAndLogIfError(ctx, rows, "GetPresenceForUsers: rows.close() failed")
for rows.Next() {
presence := &types.PresenceInternal{}
if err = rows.Scan(&presence.UserID, &presence.Presence, &presence.ClientFields.StatusMsg, &presence.LastActiveTS); err != nil {
return nil, err
}
presence.ClientFields.Presence = presence.Presence.String()
result = append(result, presence)
} }
result.ClientFields.Presence = result.Presence.String()
return result, err return result, err
} }

View file

@ -57,31 +57,23 @@ type Database struct {
} }
func (d *Database) NewDatabaseSnapshot(ctx context.Context) (*DatabaseTransaction, error) { func (d *Database) NewDatabaseSnapshot(ctx context.Context) (*DatabaseTransaction, error) {
return d.NewDatabaseTransaction(ctx) txn, err := d.DB.BeginTx(ctx, &sql.TxOptions{
// Set the isolation level so that we see a snapshot of the database.
/* // In PostgreSQL repeatable read transactions will see a snapshot taken
TODO: Repeatable read is probably the right thing to do here, // at the first query, and since the transaction is read-only it can't
but it seems to cause some problems with the invite tests, so // run into any serialisation errors.
need to investigate that further. // https://www.postgresql.org/docs/9.5/static/transaction-iso.html#XACT-REPEATABLE-READ
Isolation: sql.LevelRepeatableRead,
txn, err := d.DB.BeginTx(ctx, &sql.TxOptions{ ReadOnly: true,
// Set the isolation level so that we see a snapshot of the database. })
// In PostgreSQL repeatable read transactions will see a snapshot taken if err != nil {
// at the first query, and since the transaction is read-only it can't return nil, err
// run into any serialisation errors. }
// https://www.postgresql.org/docs/9.5/static/transaction-iso.html#XACT-REPEATABLE-READ return &DatabaseTransaction{
Isolation: sql.LevelRepeatableRead, Database: d,
ReadOnly: true, ctx: ctx,
}) txn: txn,
if err != nil { }, nil
return nil, err
}
return &DatabaseTransaction{
Database: d,
ctx: ctx,
txn: txn,
}, nil
*/
} }
func (d *Database) NewDatabaseTransaction(ctx context.Context) (*DatabaseTransaction, error) { func (d *Database) NewDatabaseTransaction(ctx context.Context) (*DatabaseTransaction, error) {
@ -572,8 +564,8 @@ func (d *Database) UpdatePresence(ctx context.Context, userID string, presence t
return pos, err return pos, err
} }
func (d *Database) GetPresence(ctx context.Context, userID string) (*types.PresenceInternal, error) { func (d *Database) GetPresences(ctx context.Context, userIDs []string) ([]*types.PresenceInternal, error) {
return d.Presence.GetPresenceForUser(ctx, nil, userID) return d.Presence.GetPresenceForUsers(ctx, nil, userIDs)
} }
func (d *Database) SelectMembershipForUser(ctx context.Context, roomID, userID string, pos int64) (membership string, topologicalPos int, err error) { func (d *Database) SelectMembershipForUser(ctx context.Context, roomID, userID string, pos int64) (membership string, topologicalPos int, err error) {

View file

@ -596,8 +596,8 @@ func (d *DatabaseTransaction) GetUserUnreadNotificationCountsForRooms(ctx contex
return d.NotificationData.SelectUserUnreadCountsForRooms(ctx, d.txn, userID, roomIDs) return d.NotificationData.SelectUserUnreadCountsForRooms(ctx, d.txn, userID, roomIDs)
} }
func (d *DatabaseTransaction) GetPresence(ctx context.Context, userID string) (*types.PresenceInternal, error) { func (d *DatabaseTransaction) GetPresences(ctx context.Context, userIDs []string) ([]*types.PresenceInternal, error) {
return d.Presence.GetPresenceForUser(ctx, d.txn, userID) return d.Presence.GetPresenceForUsers(ctx, d.txn, userIDs)
} }
func (d *DatabaseTransaction) PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error) { func (d *DatabaseTransaction) PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error) {

View file

@ -17,12 +17,14 @@ package sqlite3
import ( import (
"context" "context"
"database/sql" "database/sql"
"strings"
"time" "time"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/internal/sqlutil" "github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/dendrite/syncapi/types"
"github.com/matrix-org/gomatrixserverlib"
) )
const presenceSchema = ` const presenceSchema = `
@ -62,9 +64,9 @@ const upsertPresenceFromSyncSQL = "" +
" RETURNING id" " RETURNING id"
const selectPresenceForUserSQL = "" + const selectPresenceForUserSQL = "" +
"SELECT presence, status_msg, last_active_ts" + "SELECT user_id, presence, status_msg, last_active_ts" +
" FROM syncapi_presence" + " FROM syncapi_presence" +
" WHERE user_id = $1 LIMIT 1" " WHERE user_id IN ($1)"
const selectMaxPresenceSQL = "" + const selectMaxPresenceSQL = "" +
"SELECT COALESCE(MAX(id), 0) FROM syncapi_presence" "SELECT COALESCE(MAX(id), 0) FROM syncapi_presence"
@ -134,20 +136,38 @@ func (p *presenceStatements) UpsertPresence(
return return
} }
// GetPresenceForUser returns the current presence of a user. // GetPresenceForUsers returns the current presence for a list of users.
func (p *presenceStatements) GetPresenceForUser( // If the user doesn't have a presence status yet, it is omitted from the response.
func (p *presenceStatements) GetPresenceForUsers(
ctx context.Context, txn *sql.Tx, ctx context.Context, txn *sql.Tx,
userID string, userIDs []string,
) (*types.PresenceInternal, error) { ) ([]*types.PresenceInternal, error) {
result := &types.PresenceInternal{ qry := strings.Replace(selectPresenceForUserSQL, "($1)", sqlutil.QueryVariadic(len(userIDs)), 1)
UserID: userID, prepStmt, err := p.db.Prepare(qry)
if err != nil {
return nil, err
} }
stmt := sqlutil.TxStmt(txn, p.selectPresenceForUsersStmt) defer internal.CloseAndLogIfError(ctx, prepStmt, "GetPresenceForUsers: stmt.close() failed")
err := stmt.QueryRowContext(ctx, userID).Scan(&result.Presence, &result.ClientFields.StatusMsg, &result.LastActiveTS)
if err == sql.ErrNoRows { params := make([]interface{}, len(userIDs))
return nil, nil for i := range userIDs {
params[i] = userIDs[i]
}
rows, err := sqlutil.TxStmt(txn, prepStmt).QueryContext(ctx, params...)
if err != nil {
return nil, err
}
defer internal.CloseAndLogIfError(ctx, rows, "GetPresenceForUsers: rows.close() failed")
result := make([]*types.PresenceInternal, 0, len(userIDs))
for rows.Next() {
presence := &types.PresenceInternal{}
if err = rows.Scan(&presence.UserID, &presence.Presence, &presence.ClientFields.StatusMsg, &presence.LastActiveTS); err != nil {
return nil, err
}
presence.ClientFields.Presence = presence.Presence.String()
result = append(result, presence)
} }
result.ClientFields.Presence = result.Presence.String()
return result, err return result, err
} }

View file

@ -207,7 +207,7 @@ type Ignores interface {
type Presence interface { type Presence interface {
UpsertPresence(ctx context.Context, txn *sql.Tx, userID string, statusMsg *string, presence types.Presence, lastActiveTS gomatrixserverlib.Timestamp, fromSync bool) (pos types.StreamPosition, err error) UpsertPresence(ctx context.Context, txn *sql.Tx, userID string, statusMsg *string, presence types.Presence, lastActiveTS gomatrixserverlib.Timestamp, fromSync bool) (pos types.StreamPosition, err error)
GetPresenceForUser(ctx context.Context, txn *sql.Tx, userID string) (presence *types.PresenceInternal, err error) GetPresenceForUsers(ctx context.Context, txn *sql.Tx, userIDs []string) (presence []*types.PresenceInternal, err error)
GetMaxPresenceID(ctx context.Context, txn *sql.Tx) (pos types.StreamPosition, err error) GetMaxPresenceID(ctx context.Context, txn *sql.Tx) (pos types.StreamPosition, err error)
GetPresenceAfter(ctx context.Context, txn *sql.Tx, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (presences map[string]*types.PresenceInternal, err error) GetPresenceAfter(ctx context.Context, txn *sql.Tx, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (presences map[string]*types.PresenceInternal, err error)
} }

View file

@ -0,0 +1,136 @@
package tables_test
import (
"context"
"database/sql"
"reflect"
"testing"
"time"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/syncapi/storage/postgres"
"github.com/matrix-org/dendrite/syncapi/storage/sqlite3"
"github.com/matrix-org/dendrite/syncapi/storage/tables"
"github.com/matrix-org/dendrite/syncapi/types"
"github.com/matrix-org/dendrite/test"
)
func mustPresenceTable(t *testing.T, dbType test.DBType) (tables.Presence, func()) {
t.Helper()
connStr, close := test.PrepareDBConnectionString(t, dbType)
db, err := sqlutil.Open(&config.DatabaseOptions{
ConnectionString: config.DataSource(connStr),
}, sqlutil.NewExclusiveWriter())
if err != nil {
t.Fatalf("failed to open db: %s", err)
}
var tab tables.Presence
switch dbType {
case test.DBTypePostgres:
tab, err = postgres.NewPostgresPresenceTable(db)
case test.DBTypeSQLite:
var stream sqlite3.StreamIDStatements
if err = stream.Prepare(db); err != nil {
t.Fatalf("failed to prepare stream stmts: %s", err)
}
tab, err = sqlite3.NewSqlitePresenceTable(db, &stream)
}
if err != nil {
t.Fatalf("failed to make new table: %s", err)
}
return tab, close
}
func TestPresence(t *testing.T) {
alice := test.NewUser(t)
bob := test.NewUser(t)
ctx := context.Background()
statusMsg := "Hello World!"
timestamp := gomatrixserverlib.AsTimestamp(time.Now())
var txn *sql.Tx
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
tab, closeDB := mustPresenceTable(t, dbType)
defer closeDB()
// Insert some presences
pos, err := tab.UpsertPresence(ctx, txn, alice.ID, &statusMsg, types.PresenceOnline, timestamp, false)
if err != nil {
t.Error(err)
}
wantPos := types.StreamPosition(1)
if pos != wantPos {
t.Errorf("expected pos to be %d, got %d", wantPos, pos)
}
pos, err = tab.UpsertPresence(ctx, txn, bob.ID, &statusMsg, types.PresenceOnline, timestamp, false)
if err != nil {
t.Error(err)
}
wantPos = 2
if pos != wantPos {
t.Errorf("expected pos to be %d, got %d", wantPos, pos)
}
// verify the expected max presence ID
maxPos, err := tab.GetMaxPresenceID(ctx, txn)
if err != nil {
t.Error(err)
}
if maxPos != wantPos {
t.Errorf("expected max pos to be %d, got %d", wantPos, maxPos)
}
// This should increment the position
pos, err = tab.UpsertPresence(ctx, txn, bob.ID, &statusMsg, types.PresenceOnline, timestamp, true)
if err != nil {
t.Error(err)
}
wantPos = pos
if wantPos <= maxPos {
t.Errorf("expected pos to be %d incremented, got %d", wantPos, pos)
}
// This should return only Bobs status
presences, err := tab.GetPresenceAfter(ctx, txn, maxPos, gomatrixserverlib.EventFilter{Limit: 10})
if err != nil {
t.Error(err)
}
if c := len(presences); c > 1 {
t.Errorf("expected only one presence, got %d", c)
}
// Validate the response
wantPresence := &types.PresenceInternal{
UserID: bob.ID,
Presence: types.PresenceOnline,
StreamPos: wantPos,
LastActiveTS: timestamp,
ClientFields: types.PresenceClientResponse{
LastActiveAgo: 0,
Presence: types.PresenceOnline.String(),
StatusMsg: &statusMsg,
},
}
if !reflect.DeepEqual(wantPresence, presences[bob.ID]) {
t.Errorf("unexpected presence result:\n%+v, want\n%+v", presences[bob.ID], wantPresence)
}
// Try getting presences for existing and non-existing users
getUsers := []string{alice.ID, bob.ID, "@doesntexist:test"}
presencesForUsers, err := tab.GetPresenceForUsers(ctx, nil, getUsers)
if err != nil {
t.Error(err)
}
if len(presencesForUsers) >= len(getUsers) {
t.Errorf("expected less presences, but they are the same/more as requested: %d >= %d", len(presencesForUsers), len(getUsers))
}
})
}

View file

@ -17,6 +17,7 @@ package streams
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"sync" "sync"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
@ -70,39 +71,25 @@ func (p *PresenceStreamProvider) IncrementalSync(
return from return from
} }
if len(presences) == 0 { getPresenceForUsers, err := p.getNeededUsersFromRequest(ctx, req, presences)
if err != nil {
req.Log.WithError(err).Error("getNeededUsersFromRequest failed")
return from
}
// Got no presence between range and no presence to get from the database
if len(getPresenceForUsers) == 0 && len(presences) == 0 {
return to return to
} }
// add newly joined rooms user presences dbPresences, err := snapshot.GetPresences(ctx, getPresenceForUsers)
newlyJoined := joinedRooms(req.Response, req.Device.UserID) if err != nil {
if len(newlyJoined) > 0 { req.Log.WithError(err).Error("unable to query presence for user")
// TODO: Check if this is working better than before. _ = snapshot.Rollback()
if err = p.notifier.LoadRooms(ctx, p.DB, newlyJoined); err != nil { return from
req.Log.WithError(err).Error("unable to refresh notifier lists") }
return from for _, presence := range dbPresences {
} presences[presence.UserID] = presence
NewlyJoinedLoop:
for _, roomID := range newlyJoined {
roomUsers := p.notifier.JoinedUsers(roomID)
for i := range roomUsers {
// we already got a presence from this user
if _, ok := presences[roomUsers[i]]; ok {
continue
}
// Bear in mind that this might return nil, but at least populating
// a nil means that there's a map entry so we won't repeat this call.
presences[roomUsers[i]], err = snapshot.GetPresence(ctx, roomUsers[i])
if err != nil {
req.Log.WithError(err).Error("unable to query presence for user")
_ = snapshot.Rollback()
return from
}
if len(presences) > req.Filter.Presence.Limit {
break NewlyJoinedLoop
}
}
}
} }
lastPos := from lastPos := from
@ -164,6 +151,39 @@ func (p *PresenceStreamProvider) IncrementalSync(
return lastPos return lastPos
} }
func (p *PresenceStreamProvider) getNeededUsersFromRequest(ctx context.Context, req *types.SyncRequest, presences map[string]*types.PresenceInternal) ([]string, error) {
getPresenceForUsers := []string{}
// Add presence for users which newly joined a room
for userID := range req.MembershipChanges {
if _, ok := presences[userID]; ok {
continue
}
getPresenceForUsers = append(getPresenceForUsers, userID)
}
// add newly joined rooms user presences
newlyJoined := joinedRooms(req.Response, req.Device.UserID)
if len(newlyJoined) == 0 {
return getPresenceForUsers, nil
}
// TODO: Check if this is working better than before.
if err := p.notifier.LoadRooms(ctx, p.DB, newlyJoined); err != nil {
return getPresenceForUsers, fmt.Errorf("unable to refresh notifier lists: %w", err)
}
for _, roomID := range newlyJoined {
roomUsers := p.notifier.JoinedUsers(roomID)
for i := range roomUsers {
// we already got a presence from this user
if _, ok := presences[roomUsers[i]]; ok {
continue
}
getPresenceForUsers = append(getPresenceForUsers, roomUsers[i])
}
}
return getPresenceForUsers, nil
}
func joinedRooms(res *types.Response, userID string) []string { func joinedRooms(res *types.Response, userID string) []string {
var roomIDs []string var roomIDs []string
for roomID, join := range res.Rooms.Join { for roomID, join := range res.Rooms.Join {

View file

@ -87,8 +87,7 @@ func (p *ReceiptStreamProvider) IncrementalSync(
} }
ev := gomatrixserverlib.ClientEvent{ ev := gomatrixserverlib.ClientEvent{
Type: gomatrixserverlib.MReceipt, Type: gomatrixserverlib.MReceipt,
RoomID: roomID,
} }
content := make(map[string]ReceiptMRead) content := make(map[string]ReceiptMRead)
for _, receipt := range receipts { for _, receipt := range receipts {

View file

@ -145,12 +145,12 @@ func (rp *RequestPool) updatePresence(db storage.Presence, presence string, user
} }
// ensure we also send the current status_msg to federated servers and not nil // ensure we also send the current status_msg to federated servers and not nil
dbPresence, err := db.GetPresence(context.Background(), userID) dbPresence, err := db.GetPresences(context.Background(), []string{userID})
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
return return
} }
if dbPresence != nil { if len(dbPresence) > 0 && dbPresence[0] != nil {
newPresence.ClientFields = dbPresence.ClientFields newPresence.ClientFields = dbPresence[0].ClientFields
} }
newPresence.ClientFields.Presence = presenceID.String() newPresence.ClientFields.Presence = presenceID.String()

View file

@ -29,8 +29,8 @@ func (d dummyDB) UpdatePresence(ctx context.Context, userID string, presence typ
return 0, nil return 0, nil
} }
func (d dummyDB) GetPresence(ctx context.Context, userID string) (*types.PresenceInternal, error) { func (d dummyDB) GetPresences(ctx context.Context, userID []string) ([]*types.PresenceInternal, error) {
return &types.PresenceInternal{}, nil return []*types.PresenceInternal{}, nil
} }
func (d dummyDB) PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error) { func (d dummyDB) PresenceAfter(ctx context.Context, after types.StreamPosition, filter gomatrixserverlib.EventFilter) (map[string]*types.PresenceInternal, error) {

View file

@ -480,6 +480,13 @@ func (jr JoinResponse) MarshalJSON() ([]byte, error) {
if jr.Ephemeral != nil && len(jr.Ephemeral.Events) == 0 { if jr.Ephemeral != nil && len(jr.Ephemeral.Events) == 0 {
a.Ephemeral = nil a.Ephemeral = nil
} }
if jr.Ephemeral != nil {
// Remove the room_id from EDUs, as this seems to cause Element Web
// to trigger notifications - https://github.com/vector-im/element-web/issues/17263
for i := range jr.Ephemeral.Events {
jr.Ephemeral.Events[i].RoomID = ""
}
}
if jr.AccountData != nil && len(jr.AccountData.Events) == 0 { if jr.AccountData != nil && len(jr.AccountData.Events) == 0 {
a.AccountData = nil a.AccountData = nil
} }

View file

@ -2,6 +2,7 @@ package types
import ( import (
"encoding/json" "encoding/json"
"reflect"
"testing" "testing"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
@ -63,3 +64,102 @@ func TestNewInviteResponse(t *testing.T) {
t.Fatalf("Invite response didn't contain correct info") t.Fatalf("Invite response didn't contain correct info")
} }
} }
func TestJoinResponse_MarshalJSON(t *testing.T) {
type fields struct {
Summary *Summary
State *ClientEvents
Timeline *Timeline
Ephemeral *ClientEvents
AccountData *ClientEvents
UnreadNotifications *UnreadNotifications
}
tests := []struct {
name string
fields fields
want []byte
wantErr bool
}{
{
name: "empty state is removed",
fields: fields{
State: &ClientEvents{},
},
want: []byte("{}"),
},
{
name: "empty accountdata is removed",
fields: fields{
AccountData: &ClientEvents{},
},
want: []byte("{}"),
},
{
name: "empty ephemeral is removed",
fields: fields{
Ephemeral: &ClientEvents{},
},
want: []byte("{}"),
},
{
name: "empty timeline is removed",
fields: fields{
Timeline: &Timeline{},
},
want: []byte("{}"),
},
{
name: "empty summary is removed",
fields: fields{
Summary: &Summary{},
},
want: []byte("{}"),
},
{
name: "unread notifications are removed, if everything else is empty",
fields: fields{
UnreadNotifications: &UnreadNotifications{},
},
want: []byte("{}"),
},
{
name: "unread notifications are NOT removed, if state is set",
fields: fields{
State: &ClientEvents{Events: []gomatrixserverlib.ClientEvent{{Content: []byte("{}")}}},
UnreadNotifications: &UnreadNotifications{NotificationCount: 1},
},
want: []byte(`{"state":{"events":[{"content":{},"type":""}]},"unread_notifications":{"highlight_count":0,"notification_count":1}}`),
},
{
name: "roomID is removed from EDUs",
fields: fields{
Ephemeral: &ClientEvents{
Events: []gomatrixserverlib.ClientEvent{
{RoomID: "!someRandomRoomID:test", Content: []byte("{}")},
},
},
},
want: []byte(`{"ephemeral":{"events":[{"content":{},"type":""}]}}`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
jr := JoinResponse{
Summary: tt.fields.Summary,
State: tt.fields.State,
Timeline: tt.fields.Timeline,
Ephemeral: tt.fields.Ephemeral,
AccountData: tt.fields.AccountData,
UnreadNotifications: tt.fields.UnreadNotifications,
}
got, err := jr.MarshalJSON()
if (err != nil) != tt.wantErr {
t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("MarshalJSON() got = %v, want %v", string(got), string(tt.want))
}
})
}
}

View file

@ -108,7 +108,7 @@ func Base(cfg *config.Dendrite) (*base.BaseDendrite, nats.JetStreamContext, *nat
cfg.Global.JetStream.InMemory = true cfg.Global.JetStream.InMemory = true
cfg.SyncAPI.Fulltext.InMemory = true cfg.SyncAPI.Fulltext.InMemory = true
cfg.FederationAPI.KeyPerspectives = nil cfg.FederationAPI.KeyPerspectives = nil
base := base.NewBaseDendrite(cfg, "Tests") base := base.NewBaseDendrite(cfg, "Tests", base.DisableMetrics)
js, jc := base.NATS.Prepare(base.ProcessContext, &cfg.Global.JetStream) js, jc := base.NATS.Prepare(base.ProcessContext, &cfg.Global.JetStream)
return base, js, jc return base, js, jc
} }

View file

@ -385,7 +385,6 @@ func (s *OutputRoomEventConsumer) localRoomMembers(ctx context.Context, roomID s
req := &rsapi.QueryMembershipsForRoomRequest{ req := &rsapi.QueryMembershipsForRoomRequest{
RoomID: roomID, RoomID: roomID,
JoinedOnly: true, JoinedOnly: true,
LocalOnly: true,
} }
var res rsapi.QueryMembershipsForRoomResponse var res rsapi.QueryMembershipsForRoomResponse
@ -396,8 +395,23 @@ func (s *OutputRoomEventConsumer) localRoomMembers(ctx context.Context, roomID s
} }
var members []*localMembership var members []*localMembership
var ntotal int
for _, event := range res.JoinEvents { for _, event := range res.JoinEvents {
// Filter out invalid join events
if event.StateKey == nil {
continue
}
if *event.StateKey == "" {
continue
}
_, serverName, err := gomatrixserverlib.SplitID('@', *event.StateKey)
if err != nil {
log.WithError(err).Error("failed to get servername from statekey")
continue
}
// Only get memberships for our server
if serverName != s.serverName {
continue
}
member, err := newLocalMembership(&event) member, err := newLocalMembership(&event)
if err != nil { if err != nil {
log.WithError(err).Errorf("Parsing MemberContent") log.WithError(err).Errorf("Parsing MemberContent")
@ -410,11 +424,10 @@ func (s *OutputRoomEventConsumer) localRoomMembers(ctx context.Context, roomID s
continue continue
} }
ntotal++
members = append(members, member) members = append(members, member)
} }
return members, ntotal, nil return members, len(res.JoinEvents), nil
} }
// roomName returns the name in the event (if type==m.room.name), or // roomName returns the name in the event (if type==m.room.name), or
@ -641,7 +654,7 @@ func (s *OutputRoomEventConsumer) evaluatePushRules(ctx context.Context, event *
if rule == nil { if rule == nil {
// SPEC: If no rules match an event, the homeserver MUST NOT // SPEC: If no rules match an event, the homeserver MUST NOT
// notify the Push Gateway for that event. // notify the Push Gateway for that event.
return nil, err return nil, nil
} }
log.WithFields(log.Fields{ log.WithFields(log.Fields{

View file

@ -16,176 +16,177 @@ package inthttp
import ( import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
) )
// nolint: gocyclo // nolint: gocyclo
func AddRoutes(internalAPIMux *mux.Router, s api.UserInternalAPI) { func AddRoutes(internalAPIMux *mux.Router, s api.UserInternalAPI, enableMetrics bool) {
addRoutesLoginToken(internalAPIMux, s) addRoutesLoginToken(internalAPIMux, s, enableMetrics)
internalAPIMux.Handle( internalAPIMux.Handle(
PerformAccountCreationPath, PerformAccountCreationPath,
httputil.MakeInternalRPCAPI("UserAPIPerformAccountCreation", s.PerformAccountCreation), httputil.MakeInternalRPCAPI("UserAPIPerformAccountCreation", enableMetrics, s.PerformAccountCreation),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformPasswordUpdatePath, PerformPasswordUpdatePath,
httputil.MakeInternalRPCAPI("UserAPIPerformPasswordUpdate", s.PerformPasswordUpdate), httputil.MakeInternalRPCAPI("UserAPIPerformPasswordUpdate", enableMetrics, s.PerformPasswordUpdate),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformDeviceCreationPath, PerformDeviceCreationPath,
httputil.MakeInternalRPCAPI("UserAPIPerformDeviceCreation", s.PerformDeviceCreation), httputil.MakeInternalRPCAPI("UserAPIPerformDeviceCreation", enableMetrics, s.PerformDeviceCreation),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformLastSeenUpdatePath, PerformLastSeenUpdatePath,
httputil.MakeInternalRPCAPI("UserAPIPerformLastSeenUpdate", s.PerformLastSeenUpdate), httputil.MakeInternalRPCAPI("UserAPIPerformLastSeenUpdate", enableMetrics, s.PerformLastSeenUpdate),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformDeviceUpdatePath, PerformDeviceUpdatePath,
httputil.MakeInternalRPCAPI("UserAPIPerformDeviceUpdate", s.PerformDeviceUpdate), httputil.MakeInternalRPCAPI("UserAPIPerformDeviceUpdate", enableMetrics, s.PerformDeviceUpdate),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformDeviceDeletionPath, PerformDeviceDeletionPath,
httputil.MakeInternalRPCAPI("UserAPIPerformDeviceDeletion", s.PerformDeviceDeletion), httputil.MakeInternalRPCAPI("UserAPIPerformDeviceDeletion", enableMetrics, s.PerformDeviceDeletion),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformAccountDeactivationPath, PerformAccountDeactivationPath,
httputil.MakeInternalRPCAPI("UserAPIPerformAccountDeactivation", s.PerformAccountDeactivation), httputil.MakeInternalRPCAPI("UserAPIPerformAccountDeactivation", enableMetrics, s.PerformAccountDeactivation),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformOpenIDTokenCreationPath, PerformOpenIDTokenCreationPath,
httputil.MakeInternalRPCAPI("UserAPIPerformOpenIDTokenCreation", s.PerformOpenIDTokenCreation), httputil.MakeInternalRPCAPI("UserAPIPerformOpenIDTokenCreation", enableMetrics, s.PerformOpenIDTokenCreation),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryProfilePath, QueryProfilePath,
httputil.MakeInternalRPCAPI("UserAPIQueryProfile", s.QueryProfile), httputil.MakeInternalRPCAPI("UserAPIQueryProfile", enableMetrics, s.QueryProfile),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryAccessTokenPath, QueryAccessTokenPath,
httputil.MakeInternalRPCAPI("UserAPIQueryAccessToken", s.QueryAccessToken), httputil.MakeInternalRPCAPI("UserAPIQueryAccessToken", enableMetrics, s.QueryAccessToken),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryDevicesPath, QueryDevicesPath,
httputil.MakeInternalRPCAPI("UserAPIQueryDevices", s.QueryDevices), httputil.MakeInternalRPCAPI("UserAPIQueryDevices", enableMetrics, s.QueryDevices),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryAccountDataPath, QueryAccountDataPath,
httputil.MakeInternalRPCAPI("UserAPIQueryAccountData", s.QueryAccountData), httputil.MakeInternalRPCAPI("UserAPIQueryAccountData", enableMetrics, s.QueryAccountData),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryDeviceInfosPath, QueryDeviceInfosPath,
httputil.MakeInternalRPCAPI("UserAPIQueryDeviceInfos", s.QueryDeviceInfos), httputil.MakeInternalRPCAPI("UserAPIQueryDeviceInfos", enableMetrics, s.QueryDeviceInfos),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QuerySearchProfilesPath, QuerySearchProfilesPath,
httputil.MakeInternalRPCAPI("UserAPIQuerySearchProfiles", s.QuerySearchProfiles), httputil.MakeInternalRPCAPI("UserAPIQuerySearchProfiles", enableMetrics, s.QuerySearchProfiles),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryOpenIDTokenPath, QueryOpenIDTokenPath,
httputil.MakeInternalRPCAPI("UserAPIQueryOpenIDToken", s.QueryOpenIDToken), httputil.MakeInternalRPCAPI("UserAPIQueryOpenIDToken", enableMetrics, s.QueryOpenIDToken),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
InputAccountDataPath, InputAccountDataPath,
httputil.MakeInternalRPCAPI("UserAPIInputAccountData", s.InputAccountData), httputil.MakeInternalRPCAPI("UserAPIInputAccountData", enableMetrics, s.InputAccountData),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryKeyBackupPath, QueryKeyBackupPath,
httputil.MakeInternalRPCAPI("UserAPIQueryKeyBackup", s.QueryKeyBackup), httputil.MakeInternalRPCAPI("UserAPIQueryKeyBackup", enableMetrics, s.QueryKeyBackup),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformKeyBackupPath, PerformKeyBackupPath,
httputil.MakeInternalRPCAPI("UserAPIPerformKeyBackup", s.PerformKeyBackup), httputil.MakeInternalRPCAPI("UserAPIPerformKeyBackup", enableMetrics, s.PerformKeyBackup),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryNotificationsPath, QueryNotificationsPath,
httputil.MakeInternalRPCAPI("UserAPIQueryNotifications", s.QueryNotifications), httputil.MakeInternalRPCAPI("UserAPIQueryNotifications", enableMetrics, s.QueryNotifications),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformPusherSetPath, PerformPusherSetPath,
httputil.MakeInternalRPCAPI("UserAPIPerformPusherSet", s.PerformPusherSet), httputil.MakeInternalRPCAPI("UserAPIPerformPusherSet", enableMetrics, s.PerformPusherSet),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformPusherDeletionPath, PerformPusherDeletionPath,
httputil.MakeInternalRPCAPI("UserAPIPerformPusherDeletion", s.PerformPusherDeletion), httputil.MakeInternalRPCAPI("UserAPIPerformPusherDeletion", enableMetrics, s.PerformPusherDeletion),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryPushersPath, QueryPushersPath,
httputil.MakeInternalRPCAPI("UserAPIQueryPushers", s.QueryPushers), httputil.MakeInternalRPCAPI("UserAPIQueryPushers", enableMetrics, s.QueryPushers),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformPushRulesPutPath, PerformPushRulesPutPath,
httputil.MakeInternalRPCAPI("UserAPIPerformPushRulesPut", s.PerformPushRulesPut), httputil.MakeInternalRPCAPI("UserAPIPerformPushRulesPut", enableMetrics, s.PerformPushRulesPut),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryPushRulesPath, QueryPushRulesPath,
httputil.MakeInternalRPCAPI("UserAPIQueryPushRules", s.QueryPushRules), httputil.MakeInternalRPCAPI("UserAPIQueryPushRules", enableMetrics, s.QueryPushRules),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformSetAvatarURLPath, PerformSetAvatarURLPath,
httputil.MakeInternalRPCAPI("UserAPIPerformSetAvatarURL", s.SetAvatarURL), httputil.MakeInternalRPCAPI("UserAPIPerformSetAvatarURL", enableMetrics, s.SetAvatarURL),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryNumericLocalpartPath, QueryNumericLocalpartPath,
httputil.MakeInternalRPCAPI("UserAPIQueryNumericLocalpart", s.QueryNumericLocalpart), httputil.MakeInternalRPCAPI("UserAPIQueryNumericLocalpart", enableMetrics, s.QueryNumericLocalpart),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryAccountAvailabilityPath, QueryAccountAvailabilityPath,
httputil.MakeInternalRPCAPI("UserAPIQueryAccountAvailability", s.QueryAccountAvailability), httputil.MakeInternalRPCAPI("UserAPIQueryAccountAvailability", enableMetrics, s.QueryAccountAvailability),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryAccountByPasswordPath, QueryAccountByPasswordPath,
httputil.MakeInternalRPCAPI("UserAPIQueryAccountByPassword", s.QueryAccountByPassword), httputil.MakeInternalRPCAPI("UserAPIQueryAccountByPassword", enableMetrics, s.QueryAccountByPassword),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformSetDisplayNamePath, PerformSetDisplayNamePath,
httputil.MakeInternalRPCAPI("UserAPISetDisplayName", s.SetDisplayName), httputil.MakeInternalRPCAPI("UserAPISetDisplayName", enableMetrics, s.SetDisplayName),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryLocalpartForThreePIDPath, QueryLocalpartForThreePIDPath,
httputil.MakeInternalRPCAPI("UserAPIQueryLocalpartForThreePID", s.QueryLocalpartForThreePID), httputil.MakeInternalRPCAPI("UserAPIQueryLocalpartForThreePID", enableMetrics, s.QueryLocalpartForThreePID),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryThreePIDsForLocalpartPath, QueryThreePIDsForLocalpartPath,
httputil.MakeInternalRPCAPI("UserAPIQueryThreePIDsForLocalpart", s.QueryThreePIDsForLocalpart), httputil.MakeInternalRPCAPI("UserAPIQueryThreePIDsForLocalpart", enableMetrics, s.QueryThreePIDsForLocalpart),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformForgetThreePIDPath, PerformForgetThreePIDPath,
httputil.MakeInternalRPCAPI("UserAPIPerformForgetThreePID", s.PerformForgetThreePID), httputil.MakeInternalRPCAPI("UserAPIPerformForgetThreePID", enableMetrics, s.PerformForgetThreePID),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformSaveThreePIDAssociationPath, PerformSaveThreePIDAssociationPath,
httputil.MakeInternalRPCAPI("UserAPIPerformSaveThreePIDAssociation", s.PerformSaveThreePIDAssociation), httputil.MakeInternalRPCAPI("UserAPIPerformSaveThreePIDAssociation", enableMetrics, s.PerformSaveThreePIDAssociation),
) )
} }

View file

@ -16,24 +16,25 @@ package inthttp
import ( import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
) )
// addRoutesLoginToken adds routes for all login token API calls. // addRoutesLoginToken adds routes for all login token API calls.
func addRoutesLoginToken(internalAPIMux *mux.Router, s api.UserInternalAPI) { func addRoutesLoginToken(internalAPIMux *mux.Router, s api.UserInternalAPI, enableMetrics bool) {
internalAPIMux.Handle( internalAPIMux.Handle(
PerformLoginTokenCreationPath, PerformLoginTokenCreationPath,
httputil.MakeInternalRPCAPI("UserAPIPerformLoginTokenCreation", s.PerformLoginTokenCreation), httputil.MakeInternalRPCAPI("UserAPIPerformLoginTokenCreation", enableMetrics, s.PerformLoginTokenCreation),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
PerformLoginTokenDeletionPath, PerformLoginTokenDeletionPath,
httputil.MakeInternalRPCAPI("UserAPIPerformLoginTokenDeletion", s.PerformLoginTokenDeletion), httputil.MakeInternalRPCAPI("UserAPIPerformLoginTokenDeletion", enableMetrics, s.PerformLoginTokenDeletion),
) )
internalAPIMux.Handle( internalAPIMux.Handle(
QueryLoginTokenPath, QueryLoginTokenPath,
httputil.MakeInternalRPCAPI("UserAPIQueryLoginToken", s.QueryLoginToken), httputil.MakeInternalRPCAPI("UserAPIQueryLoginToken", enableMetrics, s.QueryLoginToken),
) )
} }

View file

@ -37,8 +37,8 @@ import (
// AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions // AddInternalRoutes registers HTTP handlers for the internal API. Invokes functions
// on the given input API. // on the given input API.
func AddInternalRoutes(router *mux.Router, intAPI api.UserInternalAPI) { func AddInternalRoutes(router *mux.Router, intAPI api.UserInternalAPI, enableMetrics bool) {
inthttp.AddRoutes(router, intAPI) inthttp.AddRoutes(router, intAPI, enableMetrics)
} }
// NewInternalAPI returns a concerete implementation of the internal API. Callers // NewInternalAPI returns a concerete implementation of the internal API. Callers

View file

@ -27,14 +27,13 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/test" "github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/test/testrig" "github.com/matrix-org/dendrite/test/testrig"
"github.com/matrix-org/dendrite/userapi" "github.com/matrix-org/dendrite/userapi"
"github.com/matrix-org/dendrite/userapi/inthttp"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/dendrite/userapi/internal" "github.com/matrix-org/dendrite/userapi/internal"
"github.com/matrix-org/dendrite/userapi/inthttp"
"github.com/matrix-org/dendrite/userapi/storage" "github.com/matrix-org/dendrite/userapi/storage"
) )
@ -79,19 +78,6 @@ func MustMakeInternalAPI(t *testing.T, opts apiTestOpts, dbType test.DBType) (ap
func TestQueryProfile(t *testing.T) { func TestQueryProfile(t *testing.T) {
aliceAvatarURL := "mxc://example.com/alice" aliceAvatarURL := "mxc://example.com/alice"
aliceDisplayName := "Alice" aliceDisplayName := "Alice"
// only one DBType, since userapi.AddInternalRoutes complains about multiple prometheus counters added
userAPI, accountDB, close := MustMakeInternalAPI(t, apiTestOpts{}, test.DBTypeSQLite)
defer close()
_, err := accountDB.CreateAccount(context.TODO(), "alice", serverName, "foobar", "", api.AccountTypeUser)
if err != nil {
t.Fatalf("failed to make account: %s", err)
}
if _, _, err := accountDB.SetAvatarURL(context.TODO(), "alice", serverName, aliceAvatarURL); err != nil {
t.Fatalf("failed to set avatar url: %s", err)
}
if _, _, err := accountDB.SetDisplayName(context.TODO(), "alice", serverName, aliceDisplayName); err != nil {
t.Fatalf("failed to set display name: %s", err)
}
testCases := []struct { testCases := []struct {
req api.QueryProfileRequest req api.QueryProfileRequest
@ -142,19 +128,34 @@ func TestQueryProfile(t *testing.T) {
} }
} }
t.Run("HTTP API", func(t *testing.T) { test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
router := mux.NewRouter().PathPrefix(httputil.InternalPathPrefix).Subrouter() userAPI, accountDB, close := MustMakeInternalAPI(t, apiTestOpts{}, dbType)
userapi.AddInternalRoutes(router, userAPI) defer close()
apiURL, cancel := test.ListenAndServe(t, router, false) _, err := accountDB.CreateAccount(context.TODO(), "alice", serverName, "foobar", "", api.AccountTypeUser)
defer cancel()
httpAPI, err := inthttp.NewUserAPIClient(apiURL, &http.Client{})
if err != nil { if err != nil {
t.Fatalf("failed to create HTTP client") t.Fatalf("failed to make account: %s", err)
} }
runCases(httpAPI, true) if _, _, err := accountDB.SetAvatarURL(context.TODO(), "alice", serverName, aliceAvatarURL); err != nil {
}) t.Fatalf("failed to set avatar url: %s", err)
t.Run("Monolith", func(t *testing.T) { }
runCases(userAPI, false) if _, _, err := accountDB.SetDisplayName(context.TODO(), "alice", serverName, aliceDisplayName); err != nil {
t.Fatalf("failed to set display name: %s", err)
}
t.Run("HTTP API", func(t *testing.T) {
router := mux.NewRouter().PathPrefix(httputil.InternalPathPrefix).Subrouter()
userapi.AddInternalRoutes(router, userAPI, false)
apiURL, cancel := test.ListenAndServe(t, router, false)
defer cancel()
httpAPI, err := inthttp.NewUserAPIClient(apiURL, &http.Client{})
if err != nil {
t.Fatalf("failed to create HTTP client")
}
runCases(httpAPI, true)
})
t.Run("Monolith", func(t *testing.T) {
runCases(userAPI, false)
})
}) })
} }

119
userapi/util/notify_test.go Normal file
View file

@ -0,0 +1,119 @@
package util_test
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
"golang.org/x/crypto/bcrypt"
"github.com/matrix-org/dendrite/internal/pushgateway"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/test/testrig"
"github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/dendrite/userapi/storage"
userUtil "github.com/matrix-org/dendrite/userapi/util"
)
func TestNotifyUserCountsAsync(t *testing.T) {
alice := test.NewUser(t)
aliceLocalpart, serverName, err := gomatrixserverlib.SplitID('@', alice.ID)
if err != nil {
t.Error(err)
}
ctx := context.Background()
// Create a test room, just used to provide events
room := test.NewRoom(t, alice)
dummyEvent := room.Events()[len(room.Events())-1]
appID := util.RandomString(8)
pushKey := util.RandomString(8)
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
receivedRequest := make(chan bool, 1)
// create a test server which responds to our /notify call
srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var data pushgateway.NotifyRequest
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
t.Error(err)
}
notification := data.Notification
// Validate the request
if notification.Counts == nil {
t.Fatal("no unread notification counts in request")
}
if unread := notification.Counts.Unread; unread != 1 {
t.Errorf("expected one unread notification, got %d", unread)
}
if len(notification.Devices) == 0 {
t.Fatal("expected devices in request")
}
// We only created one push device, so access it directly
device := notification.Devices[0]
if device.AppID != appID {
t.Errorf("unexpected app_id: %s, want %s", device.AppID, appID)
}
if device.PushKey != pushKey {
t.Errorf("unexpected push_key: %s, want %s", device.PushKey, pushKey)
}
// Return empty result, otherwise the call is handled as failed
if _, err := w.Write([]byte("{}")); err != nil {
t.Error(err)
}
close(receivedRequest)
}))
defer srv.Close()
// Create DB and Dendrite base
connStr, close := test.PrepareDBConnectionString(t, dbType)
defer close()
base, _, _ := testrig.Base(nil)
defer base.Close()
db, err := storage.NewUserAPIDatabase(base, &config.DatabaseOptions{
ConnectionString: config.DataSource(connStr),
}, "test", bcrypt.MinCost, 0, 0, "")
if err != nil {
t.Error(err)
}
// Prepare pusher with our test server URL
if err := db.UpsertPusher(ctx, api.Pusher{
Kind: api.HTTPKind,
AppID: appID,
PushKey: pushKey,
Data: map[string]interface{}{
"url": srv.URL,
},
}, aliceLocalpart, serverName); err != nil {
t.Error(err)
}
// Insert a dummy event
if err := db.InsertNotification(ctx, aliceLocalpart, serverName, dummyEvent.EventID(), 0, nil, &api.Notification{
Event: gomatrixserverlib.HeaderedToClientEvent(dummyEvent, gomatrixserverlib.FormatAll),
}); err != nil {
t.Error(err)
}
// Notify the user about a new notification
if err := userUtil.NotifyUserCountsAsync(ctx, pushgateway.NewHTTPClient(true), aliceLocalpart, serverName, db); err != nil {
t.Error(err)
}
select {
case <-time.After(time.Second * 5):
t.Error("timed out waiting for response")
case <-receivedRequest:
}
})
}

View file

@ -97,12 +97,10 @@ func (p *phoneHomeStats) collect() {
// configuration information // configuration information
p.stats["federation_disabled"] = p.cfg.Global.DisableFederation p.stats["federation_disabled"] = p.cfg.Global.DisableFederation
p.stats["nats_embedded"] = true natsEmbedded := len(p.cfg.Global.JetStream.Addresses) == 0
p.stats["nats_in_memory"] = p.cfg.Global.JetStream.InMemory p.stats["nats_embedded"] = natsEmbedded
if len(p.cfg.Global.JetStream.Addresses) > 0 { p.stats["nats_in_memory"] = p.cfg.Global.JetStream.InMemory && natsEmbedded
p.stats["nats_embedded"] = false
p.stats["nats_in_memory"] = false // probably
}
if len(p.cfg.Logging) > 0 { if len(p.cfg.Logging) > 0 {
p.stats["log_level"] = p.cfg.Logging[0].Level p.stats["log_level"] = p.cfg.Logging[0].Level
} else { } else {

View file

@ -0,0 +1,84 @@
package util
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"golang.org/x/crypto/bcrypt"
"github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/test"
"github.com/matrix-org/dendrite/test/testrig"
"github.com/matrix-org/dendrite/userapi/storage"
)
func TestCollect(t *testing.T) {
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
b, _, _ := testrig.Base(nil)
connStr, closeDB := test.PrepareDBConnectionString(t, dbType)
defer closeDB()
db, err := storage.NewUserAPIDatabase(b, &config.DatabaseOptions{
ConnectionString: config.DataSource(connStr),
}, "localhost", bcrypt.MinCost, 1000, 1000, "")
if err != nil {
t.Error(err)
}
receivedRequest := make(chan struct{}, 1)
// create a test server which responds to our call
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var data map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
t.Error(err)
}
defer r.Body.Close()
if _, err := w.Write([]byte("{}")); err != nil {
t.Error(err)
}
// verify the received data matches our expectations
dbEngine, ok := data["database_engine"]
if !ok {
t.Errorf("missing database_engine in JSON request: %+v", data)
}
version, ok := data["version"]
if !ok {
t.Errorf("missing version in JSON request: %+v", data)
}
if version != internal.VersionString() {
t.Errorf("unexpected version: %q, expected %q", version, internal.VersionString())
}
switch {
case dbType == test.DBTypeSQLite && dbEngine != "SQLite":
t.Errorf("unexpected database_engine: %s", dbEngine)
case dbType == test.DBTypePostgres && dbEngine != "Postgres":
t.Errorf("unexpected database_engine: %s", dbEngine)
}
close(receivedRequest)
}))
defer srv.Close()
b.Cfg.Global.ReportStats.Endpoint = srv.URL
stats := phoneHomeStats{
prevData: timestampToRUUsage{},
serverName: "localhost",
startTime: time.Now(),
cfg: b.Cfg,
db: db,
isMonolith: false,
client: &http.Client{Timeout: time.Second},
}
stats.collect()
select {
case <-time.After(time.Second * 5):
t.Error("timed out waiting for response")
case <-receivedRequest:
}
})
}