mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-06 14:33:10 -06:00
Disable pinecone tests and relay api.
This commit is contained in:
parent
c550c2e8cb
commit
a639e1e0aa
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
|
@ -5,7 +5,7 @@
|
|||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/dendrite-monolith-server",
|
||||
"program": "${workspaceFolder}/cmd/dendrite",
|
||||
"args": [
|
||||
"-really-enable-open-registration",
|
||||
"-config",
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build wasm
|
||||
// +build wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
// JSServer exposes an HTTP-like server interface which allows JS to 'send' requests to it.
|
||||
type JSServer struct {
|
||||
// The router which will service requests
|
||||
Mux http.Handler
|
||||
}
|
||||
|
||||
// OnRequestFromJS is the function that JS will invoke when there is a new request.
|
||||
// The JS function signature is:
|
||||
//
|
||||
// function(reqString: string): Promise<{result: string, error: string}>
|
||||
//
|
||||
// Usage is like:
|
||||
//
|
||||
// const res = await global._go_js_server.fetch(reqString);
|
||||
// if (res.error) {
|
||||
// // handle error: this is a 'network' error, not a non-2xx error.
|
||||
// }
|
||||
// const rawHttpResponse = res.result;
|
||||
func (h *JSServer) OnRequestFromJS(this js.Value, args []js.Value) interface{} {
|
||||
// we HAVE to spawn a new goroutine and return immediately or else Go will deadlock
|
||||
// if this request blocks at all e.g for /sync calls
|
||||
httpStr := args[0].String()
|
||||
promise := js.Global().Get("Promise").New(js.FuncOf(func(pthis js.Value, pargs []js.Value) interface{} {
|
||||
// The initial callback code for new Promise() is also called on the critical path, which is why
|
||||
// we need to put this in an immediately invoked goroutine.
|
||||
go func() {
|
||||
resolve := pargs[0]
|
||||
resStr, err := h.handle(httpStr)
|
||||
errStr := ""
|
||||
if err != nil {
|
||||
errStr = err.Error()
|
||||
}
|
||||
resolve.Invoke(map[string]interface{}{
|
||||
"result": resStr,
|
||||
"error": errStr,
|
||||
})
|
||||
}()
|
||||
return nil
|
||||
}))
|
||||
return promise
|
||||
}
|
||||
|
||||
// handle invokes the http.ServeMux for this request and returns the raw HTTP response.
|
||||
func (h *JSServer) handle(httpStr string) (resStr string, err error) {
|
||||
req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(httpStr)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
h.Mux.ServeHTTP(w, req)
|
||||
|
||||
res := w.Result()
|
||||
var resBuffer strings.Builder
|
||||
err = res.Write(&resBuffer)
|
||||
return resBuffer.String(), err
|
||||
}
|
||||
|
||||
// ListenAndServe registers a variable in JS-land with the given namespace. This variable is
|
||||
// a function which JS-land can call to 'send' HTTP requests. The function is attached to
|
||||
// a global object called "_go_js_server". See OnRequestFromJS for more info.
|
||||
func (h *JSServer) ListenAndServe(namespace string) {
|
||||
globalName := "_go_js_server"
|
||||
// register a hook in JS-land for it to invoke stuff
|
||||
server := js.Global().Get(globalName)
|
||||
if !server.Truthy() {
|
||||
server = js.Global().Get("Object").New()
|
||||
js.Global().Set(globalName, server)
|
||||
}
|
||||
|
||||
server.Set(namespace, js.FuncOf(h.OnRequestFromJS))
|
||||
|
||||
fmt.Printf("Listening for requests from JS on function %s.%s\n", globalName, namespace)
|
||||
// Block forever to mimic http.ListenAndServe
|
||||
select {}
|
||||
}
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build wasm
|
||||
// +build wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"syscall/js"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/appservice"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||
"github.com/matrix-org/dendrite/federationapi"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
"github.com/matrix-org/dendrite/roomserver"
|
||||
"github.com/matrix-org/dendrite/setup"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/userapi"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
_ "github.com/matrix-org/go-sqlite3-js"
|
||||
|
||||
pineconeConnections "github.com/matrix-org/pinecone/connections"
|
||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||
)
|
||||
|
||||
var GitCommit string
|
||||
|
||||
func init() {
|
||||
fmt.Printf("[%s] dendrite.js starting...\n", GitCommit)
|
||||
}
|
||||
|
||||
const publicPeer = "wss://pinecone.matrix.org/public"
|
||||
const keyNameEd25519 = "_go_ed25519_key"
|
||||
|
||||
func readKeyFromLocalStorage() (key ed25519.PrivateKey, err error) {
|
||||
localforage := js.Global().Get("localforage")
|
||||
if !localforage.Truthy() {
|
||||
err = fmt.Errorf("readKeyFromLocalStorage: no localforage")
|
||||
return
|
||||
}
|
||||
// https://localforage.github.io/localForage/
|
||||
item, ok := await(localforage.Call("getItem", keyNameEd25519))
|
||||
if !ok || !item.Truthy() {
|
||||
err = fmt.Errorf("readKeyFromLocalStorage: no key in localforage")
|
||||
return
|
||||
}
|
||||
fmt.Println("Found key in localforage")
|
||||
// extract []byte and make an ed25519 key
|
||||
seed := make([]byte, 32, 32)
|
||||
js.CopyBytesToGo(seed, item)
|
||||
|
||||
return ed25519.NewKeyFromSeed(seed), nil
|
||||
}
|
||||
|
||||
func writeKeyToLocalStorage(key ed25519.PrivateKey) error {
|
||||
localforage := js.Global().Get("localforage")
|
||||
if !localforage.Truthy() {
|
||||
return fmt.Errorf("writeKeyToLocalStorage: no localforage")
|
||||
}
|
||||
|
||||
// make a Uint8Array from the key's seed
|
||||
seed := key.Seed()
|
||||
jsSeed := js.Global().Get("Uint8Array").New(len(seed))
|
||||
js.CopyBytesToJS(jsSeed, seed)
|
||||
// write it
|
||||
localforage.Call("setItem", keyNameEd25519, jsSeed)
|
||||
return nil
|
||||
}
|
||||
|
||||
// taken from https://go-review.googlesource.com/c/go/+/150917
|
||||
|
||||
// await waits until the promise v has been resolved or rejected and returns the promise's result value.
|
||||
// The boolean value ok is true if the promise has been resolved, false if it has been rejected.
|
||||
// If v is not a promise, v itself is returned as the value and ok is true.
|
||||
func await(v js.Value) (result js.Value, ok bool) {
|
||||
if v.Type() != js.TypeObject || v.Get("then").Type() != js.TypeFunction {
|
||||
return v, true
|
||||
}
|
||||
done := make(chan struct{})
|
||||
onResolve := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
result = args[0]
|
||||
ok = true
|
||||
close(done)
|
||||
return nil
|
||||
})
|
||||
defer onResolve.Release()
|
||||
onReject := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
result = args[0]
|
||||
ok = false
|
||||
close(done)
|
||||
return nil
|
||||
})
|
||||
defer onReject.Release()
|
||||
v.Call("then", onResolve, onReject)
|
||||
<-done
|
||||
return
|
||||
}
|
||||
|
||||
func generateKey() ed25519.PrivateKey {
|
||||
// attempt to look for a seed in JS-land and if it exists use it.
|
||||
priv, err := readKeyFromLocalStorage()
|
||||
if err == nil {
|
||||
fmt.Println("Read key from localStorage")
|
||||
return priv
|
||||
}
|
||||
// generate a new key
|
||||
fmt.Println(err, " : Generating new ed25519 key")
|
||||
_, priv, err = ed25519.GenerateKey(nil)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to generate ed25519 key: %s", err)
|
||||
}
|
||||
if err := writeKeyToLocalStorage(priv); err != nil {
|
||||
fmt.Println("failed to write key to localStorage: ", err)
|
||||
// non-fatal, we'll just have amnesia for a while
|
||||
}
|
||||
return priv
|
||||
}
|
||||
|
||||
func main() {
|
||||
startup()
|
||||
|
||||
// We want to block forever to let the fetch and libp2p handler serve the APIs
|
||||
select {}
|
||||
}
|
||||
|
||||
func startup() {
|
||||
sk := generateKey()
|
||||
pk := sk.Public().(ed25519.PublicKey)
|
||||
|
||||
pRouter := pineconeRouter.NewRouter(logrus.WithField("pinecone", "router"), sk, false)
|
||||
pSessions := pineconeSessions.NewSessions(logrus.WithField("pinecone", "sessions"), pRouter, []string{"matrix"})
|
||||
pManager := pineconeConnections.NewConnectionManager(pRouter)
|
||||
pManager.AddPeer("wss://pinecone.matrix.org/public")
|
||||
|
||||
cfg := &config.Dendrite{}
|
||||
cfg.Defaults(true)
|
||||
cfg.UserAPI.AccountDatabase.ConnectionString = "file:/idb/dendritejs_account.db"
|
||||
cfg.AppServiceAPI.Database.ConnectionString = "file:/idb/dendritejs_appservice.db"
|
||||
cfg.FederationAPI.Database.ConnectionString = "file:/idb/dendritejs_fedsender.db"
|
||||
cfg.MediaAPI.Database.ConnectionString = "file:/idb/dendritejs_mediaapi.db"
|
||||
cfg.RoomServer.Database.ConnectionString = "file:/idb/dendritejs_roomserver.db"
|
||||
cfg.SyncAPI.Database.ConnectionString = "file:/idb/dendritejs_syncapi.db"
|
||||
cfg.KeyServer.Database.ConnectionString = "file:/idb/dendritejs_e2ekey.db"
|
||||
cfg.Global.JetStream.StoragePath = "file:/idb/dendritejs/"
|
||||
cfg.Global.TrustedIDServers = []string{}
|
||||
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
|
||||
cfg.Global.PrivateKey = sk
|
||||
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
||||
cfg.ClientAPI.RegistrationDisabled = false
|
||||
cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled = true
|
||||
|
||||
if err := cfg.Derive(); err != nil {
|
||||
logrus.Fatalf("Failed to derive values from config: %s", err)
|
||||
}
|
||||
base := base.NewBaseDendrite(cfg)
|
||||
defer base.Close() // nolint: errcheck
|
||||
|
||||
rsAPI := roomserver.NewInternalAPI(base)
|
||||
|
||||
federation := conn.CreateFederationClient(base, pSessions)
|
||||
|
||||
serverKeyAPI := &signing.YggdrasilKeys{}
|
||||
keyRing := serverKeyAPI.KeyRing()
|
||||
|
||||
userAPI := userapi.NewInternalAPI(base, rsAPI, federation)
|
||||
|
||||
asQuery := appservice.NewInternalAPI(
|
||||
base, userAPI, rsAPI,
|
||||
)
|
||||
rsAPI.SetAppserviceAPI(asQuery)
|
||||
fedSenderAPI := federationapi.NewInternalAPI(base, federation, rsAPI, base.Caches, keyRing, true)
|
||||
rsAPI.SetFederationAPI(fedSenderAPI, keyRing)
|
||||
|
||||
monolith := setup.Monolith{
|
||||
Config: base.Cfg,
|
||||
Client: conn.CreateClient(base, pSessions),
|
||||
FedClient: federation,
|
||||
KeyRing: keyRing,
|
||||
|
||||
AppserviceAPI: asQuery,
|
||||
FederationAPI: fedSenderAPI,
|
||||
RoomserverAPI: rsAPI,
|
||||
UserAPI: userAPI,
|
||||
//ServerKeyAPI: serverKeyAPI,
|
||||
ExtPublicRoomsProvider: rooms.NewPineconeRoomProvider(pRouter, pSessions, fedSenderAPI, federation),
|
||||
}
|
||||
monolith.AddAllPublicRoutes(base)
|
||||
|
||||
httpRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||
httpRouter.PathPrefix(httputil.PublicClientPathPrefix).Handler(base.PublicClientAPIMux)
|
||||
httpRouter.PathPrefix(httputil.PublicMediaPathPrefix).Handler(base.PublicMediaAPIMux)
|
||||
|
||||
p2pRouter := pSessions.Protocol("matrix").HTTP().Mux()
|
||||
p2pRouter.Handle(httputil.PublicFederationPathPrefix, base.PublicFederationAPIMux)
|
||||
p2pRouter.Handle(httputil.PublicMediaPathPrefix, base.PublicMediaAPIMux)
|
||||
|
||||
// Expose the matrix APIs via fetch - for local traffic
|
||||
go func() {
|
||||
logrus.Info("Listening for service-worker fetch traffic")
|
||||
s := JSServer{
|
||||
Mux: httpRouter,
|
||||
}
|
||||
s.ListenAndServe("fetch")
|
||||
}()
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !wasm
|
||||
// +build !wasm
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("dendritejs: no-op when not compiling for WebAssembly")
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build wasm
|
||||
// +build wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStartup(t *testing.T) {
|
||||
startup()
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
TARGET=""
|
||||
|
||||
while getopts "ai" option
|
||||
do
|
||||
case "$option"
|
||||
in
|
||||
a) gomobile bind -v -target android -trimpath -ldflags="-s -w" github.com/matrix-org/dendrite/build/gobind-pinecone ;;
|
||||
i) gomobile bind -v -target ios -trimpath -ldflags="" -o ~/DendriteBindings/Gobind.xcframework . ;;
|
||||
*) echo "No target specified, specify -a or -i"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -1,358 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gobind
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/userutil"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conduit"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/monolith"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/relay"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||
"github.com/matrix-org/dendrite/federationapi/api"
|
||||
userapiAPI "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/pinecone/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
pineconeMulticast "github.com/matrix-org/pinecone/multicast"
|
||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||
|
||||
_ "golang.org/x/mobile/bind"
|
||||
)
|
||||
|
||||
const (
|
||||
PeerTypeRemote = pineconeRouter.PeerTypeRemote
|
||||
PeerTypeMulticast = pineconeRouter.PeerTypeMulticast
|
||||
PeerTypeBluetooth = pineconeRouter.PeerTypeBluetooth
|
||||
PeerTypeBonjour = pineconeRouter.PeerTypeBonjour
|
||||
|
||||
MaxFrameSize = types.MaxFrameSize
|
||||
)
|
||||
|
||||
// Re-export Conduit in this package for bindings.
|
||||
type Conduit struct {
|
||||
conduit.Conduit
|
||||
}
|
||||
|
||||
type DendriteMonolith struct {
|
||||
logger logrus.Logger
|
||||
p2pMonolith monolith.P2PMonolith
|
||||
StorageDirectory string
|
||||
CacheDirectory string
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) PublicKey() string {
|
||||
return m.p2pMonolith.Router.PublicKey().String()
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) BaseURL() string {
|
||||
return fmt.Sprintf("http://%s", m.p2pMonolith.Addr())
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) PeerCount(peertype int) int {
|
||||
return m.p2pMonolith.Router.PeerCount(peertype)
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) SessionCount() int {
|
||||
return len(m.p2pMonolith.Sessions.Protocol(monolith.SessionProtocol).Sessions())
|
||||
}
|
||||
|
||||
type InterfaceInfo struct {
|
||||
Name string
|
||||
Index int
|
||||
Mtu int
|
||||
Up bool
|
||||
Broadcast bool
|
||||
Loopback bool
|
||||
PointToPoint bool
|
||||
Multicast bool
|
||||
Addrs string
|
||||
}
|
||||
|
||||
type InterfaceRetriever interface {
|
||||
CacheCurrentInterfaces() int
|
||||
GetCachedInterface(index int) *InterfaceInfo
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) RegisterNetworkCallback(intfCallback InterfaceRetriever) {
|
||||
callback := func() []pineconeMulticast.InterfaceInfo {
|
||||
count := intfCallback.CacheCurrentInterfaces()
|
||||
intfs := []pineconeMulticast.InterfaceInfo{}
|
||||
for i := 0; i < count; i++ {
|
||||
iface := intfCallback.GetCachedInterface(i)
|
||||
if iface != nil {
|
||||
intfs = append(intfs, pineconeMulticast.InterfaceInfo{
|
||||
Name: iface.Name,
|
||||
Index: iface.Index,
|
||||
Mtu: iface.Mtu,
|
||||
Up: iface.Up,
|
||||
Broadcast: iface.Broadcast,
|
||||
Loopback: iface.Loopback,
|
||||
PointToPoint: iface.PointToPoint,
|
||||
Multicast: iface.Multicast,
|
||||
Addrs: iface.Addrs,
|
||||
})
|
||||
}
|
||||
}
|
||||
return intfs
|
||||
}
|
||||
m.p2pMonolith.Multicast.RegisterNetworkCallback(callback)
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) SetMulticastEnabled(enabled bool) {
|
||||
if enabled {
|
||||
m.p2pMonolith.Multicast.Start()
|
||||
} else {
|
||||
m.p2pMonolith.Multicast.Stop()
|
||||
m.DisconnectType(int(pineconeRouter.PeerTypeMulticast))
|
||||
}
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) SetStaticPeer(uri string) {
|
||||
m.p2pMonolith.ConnManager.RemovePeers()
|
||||
for _, uri := range strings.Split(uri, ",") {
|
||||
m.p2pMonolith.ConnManager.AddPeer(strings.TrimSpace(uri))
|
||||
}
|
||||
}
|
||||
|
||||
func getServerKeyFromString(nodeID string) (gomatrixserverlib.ServerName, error) {
|
||||
var nodeKey gomatrixserverlib.ServerName
|
||||
if userID, err := gomatrixserverlib.NewUserID(nodeID, false); err == nil {
|
||||
hexKey, decodeErr := hex.DecodeString(string(userID.Domain()))
|
||||
if decodeErr != nil || len(hexKey) != ed25519.PublicKeySize {
|
||||
return "", fmt.Errorf("UserID domain is not a valid ed25519 public key: %v", userID.Domain())
|
||||
} else {
|
||||
nodeKey = userID.Domain()
|
||||
}
|
||||
} else {
|
||||
hexKey, decodeErr := hex.DecodeString(nodeID)
|
||||
if decodeErr != nil || len(hexKey) != ed25519.PublicKeySize {
|
||||
return "", fmt.Errorf("Relay server uri is not a valid ed25519 public key: %v", nodeID)
|
||||
} else {
|
||||
nodeKey = gomatrixserverlib.ServerName(nodeID)
|
||||
}
|
||||
}
|
||||
|
||||
return nodeKey, nil
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) SetRelayServers(nodeID string, uris string) {
|
||||
relays := []gomatrixserverlib.ServerName{}
|
||||
for _, uri := range strings.Split(uris, ",") {
|
||||
uri = strings.TrimSpace(uri)
|
||||
if len(uri) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
nodeKey, err := getServerKeyFromString(uri)
|
||||
if err != nil {
|
||||
logrus.Errorf(err.Error())
|
||||
continue
|
||||
}
|
||||
relays = append(relays, nodeKey)
|
||||
}
|
||||
|
||||
nodeKey, err := getServerKeyFromString(nodeID)
|
||||
if err != nil {
|
||||
logrus.Errorf(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if string(nodeKey) == m.PublicKey() {
|
||||
logrus.Infof("Setting own relay servers to: %v", relays)
|
||||
m.p2pMonolith.RelayRetriever.SetRelayServers(relays)
|
||||
} else {
|
||||
relay.UpdateNodeRelayServers(
|
||||
gomatrixserverlib.ServerName(nodeKey),
|
||||
relays,
|
||||
m.p2pMonolith.BaseDendrite.Context(),
|
||||
m.p2pMonolith.GetFederationAPI(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) GetRelayServers(nodeID string) string {
|
||||
nodeKey, err := getServerKeyFromString(nodeID)
|
||||
if err != nil {
|
||||
logrus.Errorf(err.Error())
|
||||
return ""
|
||||
}
|
||||
|
||||
relaysString := ""
|
||||
if string(nodeKey) == m.PublicKey() {
|
||||
relays := m.p2pMonolith.RelayRetriever.GetRelayServers()
|
||||
|
||||
for i, relay := range relays {
|
||||
if i != 0 {
|
||||
// Append a comma to the previous entry if there is one.
|
||||
relaysString += ","
|
||||
}
|
||||
relaysString += string(relay)
|
||||
}
|
||||
} else {
|
||||
request := api.P2PQueryRelayServersRequest{Server: gomatrixserverlib.ServerName(nodeKey)}
|
||||
response := api.P2PQueryRelayServersResponse{}
|
||||
err := m.p2pMonolith.GetFederationAPI().P2PQueryRelayServers(m.p2pMonolith.BaseDendrite.Context(), &request, &response)
|
||||
if err != nil {
|
||||
logrus.Warnf("Failed obtaining list of this node's relay servers: %s", err.Error())
|
||||
return ""
|
||||
}
|
||||
|
||||
for i, relay := range response.RelayServers {
|
||||
if i != 0 {
|
||||
// Append a comma to the previous entry if there is one.
|
||||
relaysString += ","
|
||||
}
|
||||
relaysString += string(relay)
|
||||
}
|
||||
}
|
||||
|
||||
return relaysString
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) RelayingEnabled() bool {
|
||||
return m.p2pMonolith.GetRelayAPI().RelayingEnabled()
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) SetRelayingEnabled(enabled bool) {
|
||||
m.p2pMonolith.GetRelayAPI().SetRelayingEnabled(enabled)
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) DisconnectType(peertype int) {
|
||||
for _, p := range m.p2pMonolith.Router.Peers() {
|
||||
if int(peertype) == p.PeerType {
|
||||
m.p2pMonolith.Router.Disconnect(types.SwitchPortID(p.Port), nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) DisconnectZone(zone string) {
|
||||
for _, p := range m.p2pMonolith.Router.Peers() {
|
||||
if zone == p.Zone {
|
||||
m.p2pMonolith.Router.Disconnect(types.SwitchPortID(p.Port), nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) DisconnectPort(port int) {
|
||||
m.p2pMonolith.Router.Disconnect(types.SwitchPortID(port), nil)
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) Conduit(zone string, peertype int) (*Conduit, error) {
|
||||
l, r := net.Pipe()
|
||||
newConduit := Conduit{conduit.NewConduit(r, 0)}
|
||||
go func() {
|
||||
logrus.Errorf("Attempting authenticated connect")
|
||||
var port types.SwitchPortID
|
||||
var err error
|
||||
if port, err = m.p2pMonolith.Router.Connect(
|
||||
l,
|
||||
pineconeRouter.ConnectionZone(zone),
|
||||
pineconeRouter.ConnectionPeerType(peertype),
|
||||
); err != nil {
|
||||
logrus.Errorf("Authenticated connect failed: %s", err)
|
||||
_ = l.Close()
|
||||
_ = r.Close()
|
||||
_ = newConduit.Close()
|
||||
return
|
||||
}
|
||||
newConduit.SetPort(port)
|
||||
logrus.Infof("Authenticated connect succeeded (port %d)", newConduit.Port())
|
||||
}()
|
||||
return &newConduit, nil
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) RegisterUser(localpart, password string) (string, error) {
|
||||
pubkey := m.p2pMonolith.Router.PublicKey()
|
||||
userID := userutil.MakeUserID(
|
||||
localpart,
|
||||
gomatrixserverlib.ServerName(hex.EncodeToString(pubkey[:])),
|
||||
)
|
||||
userReq := &userapiAPI.PerformAccountCreationRequest{
|
||||
AccountType: userapiAPI.AccountTypeUser,
|
||||
Localpart: localpart,
|
||||
Password: password,
|
||||
}
|
||||
userRes := &userapiAPI.PerformAccountCreationResponse{}
|
||||
if err := m.p2pMonolith.GetUserAPI().PerformAccountCreation(context.Background(), userReq, userRes); err != nil {
|
||||
return userID, fmt.Errorf("userAPI.PerformAccountCreation: %w", err)
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) RegisterDevice(localpart, deviceID string) (string, error) {
|
||||
accessTokenBytes := make([]byte, 16)
|
||||
n, err := rand.Read(accessTokenBytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("rand.Read: %w", err)
|
||||
}
|
||||
loginReq := &userapiAPI.PerformDeviceCreationRequest{
|
||||
Localpart: localpart,
|
||||
DeviceID: &deviceID,
|
||||
AccessToken: hex.EncodeToString(accessTokenBytes[:n]),
|
||||
}
|
||||
loginRes := &userapiAPI.PerformDeviceCreationResponse{}
|
||||
if err := m.p2pMonolith.GetUserAPI().PerformDeviceCreation(context.Background(), loginReq, loginRes); err != nil {
|
||||
return "", fmt.Errorf("userAPI.PerformDeviceCreation: %w", err)
|
||||
}
|
||||
if !loginRes.DeviceCreated {
|
||||
return "", fmt.Errorf("device was not created")
|
||||
}
|
||||
return loginRes.Device.AccessToken, nil
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) Start() {
|
||||
keyfile := filepath.Join(m.StorageDirectory, "p2p.pem")
|
||||
oldKeyfile := filepath.Join(m.StorageDirectory, "p2p.key")
|
||||
sk, pk := monolith.GetOrCreateKey(keyfile, oldKeyfile)
|
||||
|
||||
m.logger = logrus.Logger{
|
||||
Out: BindLogger{},
|
||||
}
|
||||
m.logger.SetOutput(BindLogger{})
|
||||
logrus.SetOutput(BindLogger{})
|
||||
|
||||
m.p2pMonolith = monolith.P2PMonolith{}
|
||||
m.p2pMonolith.SetupPinecone(sk)
|
||||
|
||||
prefix := hex.EncodeToString(pk)
|
||||
cfg := monolith.GenerateDefaultConfig(sk, m.StorageDirectory, m.CacheDirectory, prefix)
|
||||
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
||||
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
|
||||
cfg.Global.JetStream.InMemory = false
|
||||
// NOTE : disabled for now since there is a 64 bit alignment panic on 32 bit systems
|
||||
// This isn't actually fixed: https://github.com/blevesearch/zapx/pull/147
|
||||
cfg.SyncAPI.Fulltext.Enabled = false
|
||||
|
||||
enableRelaying := false
|
||||
enableMetrics := false
|
||||
enableWebsockets := false
|
||||
m.p2pMonolith.SetupDendrite(cfg, 65432, enableRelaying, enableMetrics, enableWebsockets)
|
||||
m.p2pMonolith.StartMonolith()
|
||||
}
|
||||
|
||||
func (m *DendriteMonolith) Stop() {
|
||||
m.p2pMonolith.Stop()
|
||||
}
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gobind
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
func TestMonolithStarts(t *testing.T) {
|
||||
monolith := DendriteMonolith{}
|
||||
monolith.Start()
|
||||
monolith.PublicKey()
|
||||
monolith.Stop()
|
||||
}
|
||||
|
||||
func TestMonolithSetRelayServers(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
nodeID string
|
||||
relays string
|
||||
expectedRelays string
|
||||
expectSelf bool
|
||||
}{
|
||||
{
|
||||
name: "assorted valid, invalid, empty & self keys",
|
||||
nodeID: "@valid:abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
|
||||
relays: "@valid:123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd,@invalid:notakey,,",
|
||||
expectedRelays: "123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
|
||||
expectSelf: true,
|
||||
},
|
||||
{
|
||||
name: "invalid node key",
|
||||
nodeID: "@invalid:notakey",
|
||||
relays: "@valid:123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd,@invalid:notakey,,",
|
||||
expectedRelays: "",
|
||||
expectSelf: false,
|
||||
},
|
||||
{
|
||||
name: "node is self",
|
||||
nodeID: "self",
|
||||
relays: "@valid:123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd,@invalid:notakey,,",
|
||||
expectedRelays: "123456123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
|
||||
expectSelf: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
monolith := DendriteMonolith{}
|
||||
monolith.Start()
|
||||
|
||||
inputRelays := tc.relays
|
||||
expectedRelays := tc.expectedRelays
|
||||
if tc.expectSelf {
|
||||
inputRelays += "," + monolith.PublicKey()
|
||||
expectedRelays += "," + monolith.PublicKey()
|
||||
}
|
||||
nodeID := tc.nodeID
|
||||
if nodeID == "self" {
|
||||
nodeID = monolith.PublicKey()
|
||||
}
|
||||
|
||||
monolith.SetRelayServers(nodeID, inputRelays)
|
||||
relays := monolith.GetRelayServers(nodeID)
|
||||
monolith.Stop()
|
||||
|
||||
if !containSameKeys(strings.Split(relays, ","), strings.Split(expectedRelays, ",")) {
|
||||
t.Fatalf("%s: expected %s got %s", tc.name, expectedRelays, relays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func containSameKeys(expected []string, actual []string) bool {
|
||||
if len(expected) != len(actual) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, expectedKey := range expected {
|
||||
hasMatch := false
|
||||
for _, actualKey := range actual {
|
||||
if actualKey == expectedKey {
|
||||
hasMatch = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasMatch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TestParseServerKey(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
serverKey string
|
||||
expectedErr bool
|
||||
expectedKey gomatrixserverlib.ServerName
|
||||
}{
|
||||
{
|
||||
name: "valid userid as key",
|
||||
serverKey: "@valid:abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
|
||||
expectedErr: false,
|
||||
expectedKey: "abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
|
||||
},
|
||||
{
|
||||
name: "valid key",
|
||||
serverKey: "abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
|
||||
expectedErr: false,
|
||||
expectedKey: "abcdef123456abcdef123456abcdef123456abcdef123456abcdef123456abcd",
|
||||
},
|
||||
{
|
||||
name: "invalid userid key",
|
||||
serverKey: "@invalid:notakey",
|
||||
expectedErr: true,
|
||||
expectedKey: "",
|
||||
},
|
||||
{
|
||||
name: "invalid key",
|
||||
serverKey: "@invalid:notakey",
|
||||
expectedErr: true,
|
||||
expectedKey: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
key, err := getServerKeyFromString(tc.serverKey)
|
||||
if tc.expectedErr && err == nil {
|
||||
t.Fatalf("%s: expected an error", tc.name)
|
||||
} else if !tc.expectedErr && err != nil {
|
||||
t.Fatalf("%s: didn't expect an error: %s", tc.name, err.Error())
|
||||
}
|
||||
if tc.expectedKey != key {
|
||||
t.Fatalf("%s: keys not equal. expected: %s got: %s", tc.name, tc.expectedKey, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build ios
|
||||
// +build ios
|
||||
|
||||
package gobind
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
#import <Foundation/Foundation.h>
|
||||
void Log(const char *text) {
|
||||
NSString *nss = [NSString stringWithUTF8String:text];
|
||||
NSLog(@"%@", nss);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
type BindLogger struct {
|
||||
}
|
||||
|
||||
func (nsl BindLogger) Write(p []byte) (n int, err error) {
|
||||
p = append(p, 0)
|
||||
cstr := (*C.char)(unsafe.Pointer(&p[0]))
|
||||
C.Log(cstr)
|
||||
return len(p), nil
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !ios
|
||||
// +build !ios
|
||||
|
||||
package gobind
|
||||
|
||||
import "log"
|
||||
|
||||
type BindLogger struct{}
|
||||
|
||||
func (nsl BindLogger) Write(p []byte) (n int, err error) {
|
||||
log.Println(string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
# Pinecone Demo
|
||||
|
||||
This is the Dendrite Pinecone demo! It's easy to get started.
|
||||
|
||||
To run the homeserver, start at the root of the Dendrite repository and run:
|
||||
|
||||
```
|
||||
go run ./cmd/dendrite-demo-pinecone
|
||||
```
|
||||
|
||||
To connect to the static Pinecone peer used by the mobile demos run:
|
||||
|
||||
```
|
||||
go run ./cmd/dendrite-demo-pinecone -peer wss://pinecone.matrix.org/public
|
||||
```
|
||||
|
||||
The following command line arguments are accepted:
|
||||
|
||||
* `-peer tcp://a.b.c.d:e` to specify a static Pinecone peer to connect to - you will need to supply this if you do not have another Pinecone node on your network
|
||||
* `-port 12345` to specify a port to listen on for client connections
|
||||
|
||||
Then point your favourite Matrix client to the homeserver URL`http://localhost:8008` (or whichever `-port` you specified), create an account and log in.
|
||||
|
||||
If your peering connection is operational then you should see a `Connected TCP:` line in the log output. If not then try a different peer.
|
||||
|
||||
Once logged in, you should be able to open the room directory or join a room by its ID.
|
||||
|
||||
## Store & Forward Relays
|
||||
|
||||
To test out the store & forward relay functionality, you need a minimum of 3 instances.
|
||||
One instance will act as the relay, and the other two instances will be the users trying to communicate.
|
||||
Then you can send messages between the two nodes and watch as the relay is used if the receiving node is offline.
|
||||
|
||||
### Launching the Nodes
|
||||
|
||||
Relay Server:
|
||||
```
|
||||
go run cmd/dendrite-demo-pinecone/main.go -dir relay/ -listen "[::]:49000"
|
||||
```
|
||||
|
||||
Node 1:
|
||||
```
|
||||
go run cmd/dendrite-demo-pinecone/main.go -dir node-1/ -peer "[::]:49000" -port 8007
|
||||
```
|
||||
|
||||
Node 2:
|
||||
```
|
||||
go run cmd/dendrite-demo-pinecone/main.go -dir node-2/ -peer "[::]:49000" -port 8009
|
||||
```
|
||||
|
||||
### Database Setup
|
||||
|
||||
At the moment, the database must be manually configured.
|
||||
For both `Node 1` and `Node 2` add the following entries to their respective `relay_server` table in the federationapi database:
|
||||
```
|
||||
server_name: {node_1_public_key}, relay_server_name: {relay_public_key}
|
||||
server_name: {node_2_public_key}, relay_server_name: {relay_public_key}
|
||||
```
|
||||
|
||||
After editing the database you will need to relaunch the nodes for the changes to be picked up by dendrite.
|
||||
|
||||
### Testing
|
||||
|
||||
Now you can run two separate instances of element and connect them to `Node 1` and `Node 2`.
|
||||
You can shutdown one of the nodes and continue sending messages. If you wait long enough, the message will be sent to the relay server. (you can see this in the log output of the relay server)
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package conduit
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/matrix-org/pinecone/types"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
type Conduit struct {
|
||||
closed atomic.Bool
|
||||
conn net.Conn
|
||||
portMutex sync.Mutex
|
||||
port types.SwitchPortID
|
||||
}
|
||||
|
||||
func NewConduit(conn net.Conn, port int) Conduit {
|
||||
return Conduit{
|
||||
conn: conn,
|
||||
port: types.SwitchPortID(port),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conduit) Port() int {
|
||||
c.portMutex.Lock()
|
||||
defer c.portMutex.Unlock()
|
||||
return int(c.port)
|
||||
}
|
||||
|
||||
func (c *Conduit) SetPort(port types.SwitchPortID) {
|
||||
c.portMutex.Lock()
|
||||
defer c.portMutex.Unlock()
|
||||
c.port = port
|
||||
}
|
||||
|
||||
func (c *Conduit) Read(b []byte) (int, error) {
|
||||
if c.closed.Load() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return c.conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *Conduit) ReadCopy() ([]byte, error) {
|
||||
if c.closed.Load() {
|
||||
return nil, io.EOF
|
||||
}
|
||||
var buf [65535 * 2]byte
|
||||
n, err := c.conn.Read(buf[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf[:n], nil
|
||||
}
|
||||
|
||||
func (c *Conduit) Write(b []byte) (int, error) {
|
||||
if c.closed.Load() {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return c.conn.Write(b)
|
||||
}
|
||||
|
||||
func (c *Conduit) Close() error {
|
||||
if c.closed.Load() {
|
||||
return io.ErrClosedPipe
|
||||
}
|
||||
c.closed.Store(true)
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package conduit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var TestBuf = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
type TestNetConn struct {
|
||||
net.Conn
|
||||
shouldFail bool
|
||||
}
|
||||
|
||||
func (t *TestNetConn) Read(b []byte) (int, error) {
|
||||
if t.shouldFail {
|
||||
return 0, fmt.Errorf("Failed")
|
||||
} else {
|
||||
n := copy(b, TestBuf)
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TestNetConn) Write(b []byte) (int, error) {
|
||||
if t.shouldFail {
|
||||
return 0, fmt.Errorf("Failed")
|
||||
} else {
|
||||
return len(b), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TestNetConn) Close() error {
|
||||
if t.shouldFail {
|
||||
return fmt.Errorf("Failed")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestConduitStoresPort(t *testing.T) {
|
||||
conduit := Conduit{port: 7}
|
||||
assert.Equal(t, 7, conduit.Port())
|
||||
}
|
||||
|
||||
func TestConduitRead(t *testing.T) {
|
||||
conduit := Conduit{conn: &TestNetConn{}}
|
||||
b := make([]byte, len(TestBuf))
|
||||
bytes, err := conduit.Read(b)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(TestBuf), bytes)
|
||||
assert.Equal(t, TestBuf, b)
|
||||
}
|
||||
|
||||
func TestConduitReadCopy(t *testing.T) {
|
||||
conduit := Conduit{conn: &TestNetConn{}}
|
||||
result, err := conduit.ReadCopy()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TestBuf, result)
|
||||
}
|
||||
|
||||
func TestConduitWrite(t *testing.T) {
|
||||
conduit := Conduit{conn: &TestNetConn{}}
|
||||
bytes, err := conduit.Write(TestBuf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(TestBuf), bytes)
|
||||
}
|
||||
|
||||
func TestConduitClose(t *testing.T) {
|
||||
conduit := Conduit{conn: &TestNetConn{}}
|
||||
err := conduit.Close()
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, conduit.closed.Load())
|
||||
}
|
||||
|
||||
func TestConduitReadClosed(t *testing.T) {
|
||||
conduit := Conduit{conn: &TestNetConn{}}
|
||||
err := conduit.Close()
|
||||
assert.NoError(t, err)
|
||||
b := make([]byte, len(TestBuf))
|
||||
_, err = conduit.Read(b)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestConduitReadCopyClosed(t *testing.T) {
|
||||
conduit := Conduit{conn: &TestNetConn{}}
|
||||
err := conduit.Close()
|
||||
assert.NoError(t, err)
|
||||
_, err = conduit.ReadCopy()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestConduitWriteClosed(t *testing.T) {
|
||||
conduit := Conduit{conn: &TestNetConn{}}
|
||||
err := conduit.Close()
|
||||
assert.NoError(t, err)
|
||||
_, err = conduit.Write(TestBuf)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestConduitReadCopyFails(t *testing.T) {
|
||||
conduit := Conduit{conn: &TestNetConn{shouldFail: true}}
|
||||
_, err := conduit.ReadCopy()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package conn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"nhooyr.io/websocket"
|
||||
|
||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||
)
|
||||
|
||||
func ConnectToPeer(pRouter *pineconeRouter.Router, peer string) error {
|
||||
var parent net.Conn
|
||||
if strings.HasPrefix(peer, "ws://") || strings.HasPrefix(peer, "wss://") {
|
||||
ctx := context.Background()
|
||||
c, _, err := websocket.Dial(ctx, peer, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("websocket.DefaultDialer.Dial: %w", err)
|
||||
}
|
||||
parent = websocket.NetConn(ctx, c, websocket.MessageBinary)
|
||||
} else {
|
||||
var err error
|
||||
parent, err = net.Dial("tcp", peer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("net.Dial: %w", err)
|
||||
}
|
||||
}
|
||||
if parent == nil {
|
||||
return fmt.Errorf("failed to wrap connection")
|
||||
}
|
||||
_, err := pRouter.Connect(
|
||||
parent,
|
||||
pineconeRouter.ConnectionZone("static"),
|
||||
pineconeRouter.ConnectionPeerType(pineconeRouter.PeerTypeRemote),
|
||||
pineconeRouter.ConnectionURI(peer),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
type RoundTripper struct {
|
||||
inner *http.Transport
|
||||
}
|
||||
|
||||
func (y *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req.URL.Scheme = "http"
|
||||
return y.inner.RoundTrip(req)
|
||||
}
|
||||
|
||||
func createTransport(s *pineconeSessions.Sessions) *http.Transport {
|
||||
proto := s.Protocol("matrix")
|
||||
tr := &http.Transport{
|
||||
DisableKeepAlives: false,
|
||||
Dial: proto.Dial,
|
||||
DialContext: proto.DialContext,
|
||||
DialTLS: proto.DialTLS,
|
||||
DialTLSContext: proto.DialTLSContext,
|
||||
}
|
||||
tr.RegisterProtocol(
|
||||
"matrix", &RoundTripper{
|
||||
inner: &http.Transport{
|
||||
DisableKeepAlives: false,
|
||||
Dial: proto.Dial,
|
||||
DialContext: proto.DialContext,
|
||||
DialTLS: proto.DialTLS,
|
||||
DialTLSContext: proto.DialTLSContext,
|
||||
},
|
||||
},
|
||||
)
|
||||
return tr
|
||||
}
|
||||
|
||||
func CreateClient(
|
||||
base *base.BaseDendrite, s *pineconeSessions.Sessions,
|
||||
) *gomatrixserverlib.Client {
|
||||
return gomatrixserverlib.NewClient(
|
||||
gomatrixserverlib.WithTransport(createTransport(s)),
|
||||
)
|
||||
}
|
||||
|
||||
func CreateFederationClient(
|
||||
base *base.BaseDendrite, s *pineconeSessions.Sessions,
|
||||
) *gomatrixserverlib.FederationClient {
|
||||
return gomatrixserverlib.NewFederationClient(
|
||||
base.Cfg.Global.SigningIdentities(),
|
||||
gomatrixserverlib.WithTransport(createTransport(s)),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package conn
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func WrapWebSocketConn(c *websocket.Conn) *WebSocketConn {
|
||||
return &WebSocketConn{c: c}
|
||||
}
|
||||
|
||||
type WebSocketConn struct {
|
||||
r io.Reader
|
||||
c *websocket.Conn
|
||||
}
|
||||
|
||||
func (c *WebSocketConn) Write(p []byte) (int, error) {
|
||||
err := c.c.WriteMessage(websocket.BinaryMessage, p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *WebSocketConn) Read(p []byte) (int, error) {
|
||||
for {
|
||||
if c.r == nil {
|
||||
// Advance to next message.
|
||||
var err error
|
||||
_, c.r, err = c.c.NextReader()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
n, err := c.r.Read(p)
|
||||
if err == io.EOF {
|
||||
// At end of message.
|
||||
c.r = nil
|
||||
if n > 0 {
|
||||
return n, nil
|
||||
} else {
|
||||
// No data read, continue to next message.
|
||||
continue
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *WebSocketConn) Close() error {
|
||||
return c.c.Close()
|
||||
}
|
||||
|
||||
func (c *WebSocketConn) LocalAddr() net.Addr {
|
||||
return c.c.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *WebSocketConn) RemoteAddr() net.Addr {
|
||||
return c.c.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *WebSocketConn) SetDeadline(t time.Time) error {
|
||||
if err := c.SetReadDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.SetWriteDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *WebSocketConn) SetReadDeadline(t time.Time) error {
|
||||
return c.c.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *WebSocketConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.c.SetWriteDeadline(t)
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package defaults
|
||||
|
||||
import "github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
var DefaultServerNames = map[gomatrixserverlib.ServerName]struct{}{
|
||||
"3bf0258d23c60952639cc4c69c71d1508a7d43a0475d9000ff900a1848411ec7": {},
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build elementweb
|
||||
// +build elementweb
|
||||
|
||||
package embed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
// From within the Element Web directory:
|
||||
// go run github.com/mjibson/esc -o /path/to/dendrite/internal/embed/fs_elementweb.go -private -pkg embed .
|
||||
|
||||
var cssFile = regexp.MustCompile("\\.css$")
|
||||
var jsFile = regexp.MustCompile("\\.js$")
|
||||
|
||||
type mimeFixingHandler struct {
|
||||
fs http.Handler
|
||||
}
|
||||
|
||||
func (h mimeFixingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ruri := r.RequestURI
|
||||
fmt.Println(ruri)
|
||||
switch {
|
||||
case cssFile.MatchString(ruri):
|
||||
w.Header().Set("Content-Type", "text/css")
|
||||
case jsFile.MatchString(ruri):
|
||||
w.Header().Set("Content-Type", "application/javascript")
|
||||
default:
|
||||
}
|
||||
h.fs.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func Embed(rootMux *mux.Router, listenPort int, serverName string) {
|
||||
embeddedFS := _escFS(false)
|
||||
embeddedServ := mimeFixingHandler{http.FileServer(embeddedFS)}
|
||||
|
||||
rootMux.NotFoundHandler = embeddedServ
|
||||
rootMux.HandleFunc("/config.json", func(w http.ResponseWriter, r *http.Request) {
|
||||
url := fmt.Sprintf("http://%s:%d", r.Header("Host"), listenPort)
|
||||
configFile, err := embeddedFS.Open("/config.sample.json")
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, "Couldn't open the file: "+err.Error())
|
||||
return
|
||||
}
|
||||
configFileInfo, err := configFile.Stat()
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, "Couldn't stat the file: "+err.Error())
|
||||
return
|
||||
}
|
||||
buf := make([]byte, configFileInfo.Size())
|
||||
n, err := configFile.Read(buf)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, "Couldn't read the file: "+err.Error())
|
||||
return
|
||||
}
|
||||
if int64(n) != configFileInfo.Size() {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, "The returned file size didn't match what we expected")
|
||||
return
|
||||
}
|
||||
js, _ := sjson.SetBytes(buf, "default_server_config.m\\.homeserver.base_url", url)
|
||||
js, _ = sjson.SetBytes(js, "default_server_config.m\\.homeserver.server_name", serverName)
|
||||
js, _ = sjson.SetBytes(js, "brand", fmt.Sprintf("Element %s", serverName))
|
||||
js, _ = sjson.SetBytes(js, "disable_guests", true)
|
||||
js, _ = sjson.SetBytes(js, "disable_3pid_login", true)
|
||||
js, _ = sjson.DeleteBytes(js, "welcomeUserId")
|
||||
_, _ = w.Write(js)
|
||||
})
|
||||
|
||||
fmt.Println("*-------------------------------*")
|
||||
fmt.Println("| This build includes Element Web! |")
|
||||
fmt.Println("*-------------------------------*")
|
||||
fmt.Println("Point your browser to:", url)
|
||||
fmt.Println()
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !elementweb
|
||||
// +build !elementweb
|
||||
|
||||
package embed
|
||||
|
||||
import "github.com/gorilla/mux"
|
||||
|
||||
func Embed(_ *mux.Router, _ int, _ string) {
|
||||
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/monolith"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/setup"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||
)
|
||||
|
||||
var (
|
||||
instanceName = flag.String("name", "dendrite-p2p-pinecone", "the name of this P2P demo instance")
|
||||
instancePort = flag.Int("port", 8008, "the port that the client API will listen on")
|
||||
instancePeer = flag.String("peer", "", "the static Pinecone peers to connect to, comma separated-list")
|
||||
instanceListen = flag.String("listen", ":0", "the port Pinecone peers can connect to")
|
||||
instanceDir = flag.String("dir", ".", "the directory to store the databases in (if --config not specified)")
|
||||
instanceRelayingEnabled = flag.Bool("relay", false, "whether to enable store & forward relaying for other nodes")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
internal.SetupPprof()
|
||||
|
||||
var pk ed25519.PublicKey
|
||||
var sk ed25519.PrivateKey
|
||||
|
||||
// iterate through the cli args and check if the config flag was set
|
||||
configFlagSet := false
|
||||
for _, arg := range os.Args {
|
||||
if arg == "--config" || arg == "-config" {
|
||||
configFlagSet = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var cfg *config.Dendrite
|
||||
|
||||
// use custom config if config flag is set
|
||||
if configFlagSet {
|
||||
cfg = setup.ParseFlags(true)
|
||||
sk = cfg.Global.PrivateKey
|
||||
pk = sk.Public().(ed25519.PublicKey)
|
||||
} else {
|
||||
keyfile := filepath.Join(*instanceDir, *instanceName) + ".pem"
|
||||
oldKeyfile := *instanceName + ".key"
|
||||
sk, pk = monolith.GetOrCreateKey(keyfile, oldKeyfile)
|
||||
cfg = monolith.GenerateDefaultConfig(sk, *instanceDir, *instanceDir, *instanceName)
|
||||
}
|
||||
|
||||
cfg.Global.ServerName = gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
||||
cfg.Global.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
|
||||
|
||||
p2pMonolith := monolith.P2PMonolith{}
|
||||
p2pMonolith.SetupPinecone(sk)
|
||||
p2pMonolith.Multicast.Start()
|
||||
|
||||
if instancePeer != nil && *instancePeer != "" {
|
||||
for _, peer := range strings.Split(*instancePeer, ",") {
|
||||
p2pMonolith.ConnManager.AddPeer(strings.Trim(peer, " \t\r\n"))
|
||||
}
|
||||
}
|
||||
|
||||
enableMetrics := true
|
||||
enableWebsockets := true
|
||||
p2pMonolith.SetupDendrite(cfg, *instancePort, *instanceRelayingEnabled, enableMetrics, enableWebsockets)
|
||||
p2pMonolith.StartMonolith()
|
||||
p2pMonolith.WaitForShutdown()
|
||||
|
||||
go func() {
|
||||
listener, err := net.Listen("tcp", *instanceListen)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Listening on", listener.Addr())
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("listener.Accept failed")
|
||||
continue
|
||||
}
|
||||
|
||||
port, err := p2pMonolith.Router.Connect(
|
||||
conn,
|
||||
pineconeRouter.ConnectionPeerType(pineconeRouter.PeerTypeRemote),
|
||||
)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("pSwitch.Connect failed")
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println("Inbound connection", conn.RemoteAddr(), "is connected to port", port)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package monolith
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"os"
|
||||
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
)
|
||||
|
||||
func GetOrCreateKey(keyfile string, oldKeyfile string) (ed25519.PrivateKey, ed25519.PublicKey) {
|
||||
var sk ed25519.PrivateKey
|
||||
var pk ed25519.PublicKey
|
||||
|
||||
if _, err := os.Stat(keyfile); os.IsNotExist(err) {
|
||||
if _, err = os.Stat(oldKeyfile); os.IsNotExist(err) {
|
||||
if err = test.NewMatrixKey(keyfile); err != nil {
|
||||
panic("failed to generate a new PEM key: " + err.Error())
|
||||
}
|
||||
if _, sk, err = config.LoadMatrixKey(keyfile, os.ReadFile); err != nil {
|
||||
panic("failed to load PEM key: " + err.Error())
|
||||
}
|
||||
if len(sk) != ed25519.PrivateKeySize {
|
||||
panic("the private key is not long enough")
|
||||
}
|
||||
} else {
|
||||
if sk, err = os.ReadFile(oldKeyfile); err != nil {
|
||||
panic("failed to read the old private key: " + err.Error())
|
||||
}
|
||||
if len(sk) != ed25519.PrivateKeySize {
|
||||
panic("the private key is not long enough")
|
||||
}
|
||||
if err = test.SaveMatrixKey(keyfile, sk); err != nil {
|
||||
panic("failed to convert the private key to PEM format: " + err.Error())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, sk, err = config.LoadMatrixKey(keyfile, os.ReadFile); err != nil {
|
||||
panic("failed to load PEM key: " + err.Error())
|
||||
}
|
||||
if len(sk) != ed25519.PrivateKeySize {
|
||||
panic("the private key is not long enough")
|
||||
}
|
||||
}
|
||||
|
||||
pk = sk.Public().(ed25519.PublicKey)
|
||||
|
||||
return sk, pk
|
||||
}
|
||||
|
|
@ -1,387 +0,0 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package monolith
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/matrix-org/dendrite/appservice"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/conn"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/embed"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/relay"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/rooms"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/users"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||
"github.com/matrix-org/dendrite/federationapi"
|
||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
"github.com/matrix-org/dendrite/federationapi/producers"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
"github.com/matrix-org/dendrite/relayapi"
|
||||
relayAPI "github.com/matrix-org/dendrite/relayapi/api"
|
||||
"github.com/matrix-org/dendrite/roomserver"
|
||||
"github.com/matrix-org/dendrite/setup"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/matrix-org/dendrite/userapi"
|
||||
userAPI "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
pineconeConnections "github.com/matrix-org/pinecone/connections"
|
||||
pineconeMulticast "github.com/matrix-org/pinecone/multicast"
|
||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||
pineconeEvents "github.com/matrix-org/pinecone/router/events"
|
||||
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||
)
|
||||
|
||||
const SessionProtocol = "matrix"
|
||||
|
||||
type P2PMonolith struct {
|
||||
BaseDendrite *base.BaseDendrite
|
||||
Sessions *pineconeSessions.Sessions
|
||||
Multicast *pineconeMulticast.Multicast
|
||||
ConnManager *pineconeConnections.ConnectionManager
|
||||
Router *pineconeRouter.Router
|
||||
EventChannel chan pineconeEvents.Event
|
||||
RelayRetriever relay.RelayServerRetriever
|
||||
|
||||
dendrite setup.Monolith
|
||||
port int
|
||||
httpMux *mux.Router
|
||||
pineconeMux *mux.Router
|
||||
httpServer *http.Server
|
||||
listener net.Listener
|
||||
httpListenAddr string
|
||||
stopHandlingEvents chan bool
|
||||
}
|
||||
|
||||
func GenerateDefaultConfig(sk ed25519.PrivateKey, storageDir string, cacheDir string, dbPrefix string) *config.Dendrite {
|
||||
cfg := config.Dendrite{}
|
||||
cfg.Defaults(config.DefaultOpts{
|
||||
Generate: true,
|
||||
SingleDatabase: true,
|
||||
})
|
||||
cfg.Global.PrivateKey = sk
|
||||
cfg.Global.JetStream.StoragePath = config.Path(fmt.Sprintf("%s/", filepath.Join(cacheDir, dbPrefix)))
|
||||
cfg.UserAPI.AccountDatabase.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-account.db", filepath.Join(storageDir, dbPrefix)))
|
||||
cfg.MediaAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-mediaapi.db", filepath.Join(storageDir, dbPrefix)))
|
||||
cfg.SyncAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-syncapi.db", filepath.Join(storageDir, dbPrefix)))
|
||||
cfg.RoomServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-roomserver.db", filepath.Join(storageDir, dbPrefix)))
|
||||
cfg.KeyServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-keyserver.db", filepath.Join(storageDir, dbPrefix)))
|
||||
cfg.FederationAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-federationsender.db", filepath.Join(storageDir, dbPrefix)))
|
||||
cfg.RelayAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-relayapi.db", filepath.Join(storageDir, dbPrefix)))
|
||||
cfg.MSCs.MSCs = []string{"msc2836", "msc2946"}
|
||||
cfg.MSCs.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s-mscs.db", filepath.Join(storageDir, dbPrefix)))
|
||||
cfg.ClientAPI.RegistrationDisabled = false
|
||||
cfg.ClientAPI.OpenRegistrationWithoutVerificationEnabled = true
|
||||
cfg.MediaAPI.BasePath = config.Path(filepath.Join(cacheDir, "media"))
|
||||
cfg.MediaAPI.AbsBasePath = config.Path(filepath.Join(cacheDir, "media"))
|
||||
cfg.SyncAPI.Fulltext.Enabled = true
|
||||
cfg.SyncAPI.Fulltext.IndexPath = config.Path(filepath.Join(cacheDir, "search"))
|
||||
if err := cfg.Derive(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) SetupPinecone(sk ed25519.PrivateKey) {
|
||||
p.EventChannel = make(chan pineconeEvents.Event)
|
||||
p.Router = pineconeRouter.NewRouter(logrus.WithField("pinecone", "router"), sk)
|
||||
p.Router.EnableHopLimiting()
|
||||
p.Router.EnableWakeupBroadcasts()
|
||||
p.Router.Subscribe(p.EventChannel)
|
||||
|
||||
p.Sessions = pineconeSessions.NewSessions(logrus.WithField("pinecone", "sessions"), p.Router, []string{SessionProtocol})
|
||||
p.Multicast = pineconeMulticast.NewMulticast(logrus.WithField("pinecone", "multicast"), p.Router)
|
||||
p.ConnManager = pineconeConnections.NewConnectionManager(p.Router, nil)
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) SetupDendrite(cfg *config.Dendrite, port int, enableRelaying bool, enableMetrics bool, enableWebsockets bool) {
|
||||
if enableMetrics {
|
||||
p.BaseDendrite = base.NewBaseDendrite(cfg)
|
||||
} else {
|
||||
p.BaseDendrite = base.NewBaseDendrite(cfg, base.DisableMetrics)
|
||||
}
|
||||
p.port = port
|
||||
p.BaseDendrite.ConfigureAdminEndpoints()
|
||||
|
||||
federation := conn.CreateFederationClient(p.BaseDendrite, p.Sessions)
|
||||
|
||||
serverKeyAPI := &signing.YggdrasilKeys{}
|
||||
keyRing := serverKeyAPI.KeyRing()
|
||||
|
||||
rsComponent := roomserver.NewInternalAPI(p.BaseDendrite)
|
||||
rsAPI := rsComponent
|
||||
fsAPI := federationapi.NewInternalAPI(
|
||||
p.BaseDendrite, federation, rsAPI, p.BaseDendrite.Caches, keyRing, true,
|
||||
)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(p.BaseDendrite, rsAPI, federation)
|
||||
|
||||
asAPI := appservice.NewInternalAPI(p.BaseDendrite, userAPI, rsAPI)
|
||||
|
||||
rsComponent.SetFederationAPI(fsAPI, keyRing)
|
||||
|
||||
userProvider := users.NewPineconeUserProvider(p.Router, p.Sessions, userAPI, federation)
|
||||
roomProvider := rooms.NewPineconeRoomProvider(p.Router, p.Sessions, fsAPI, federation)
|
||||
|
||||
js, _ := p.BaseDendrite.NATS.Prepare(p.BaseDendrite.ProcessContext, &p.BaseDendrite.Cfg.Global.JetStream)
|
||||
producer := &producers.SyncAPIProducer{
|
||||
JetStream: js,
|
||||
TopicReceiptEvent: p.BaseDendrite.Cfg.Global.JetStream.Prefixed(jetstream.OutputReceiptEvent),
|
||||
TopicSendToDeviceEvent: p.BaseDendrite.Cfg.Global.JetStream.Prefixed(jetstream.OutputSendToDeviceEvent),
|
||||
TopicTypingEvent: p.BaseDendrite.Cfg.Global.JetStream.Prefixed(jetstream.OutputTypingEvent),
|
||||
TopicPresenceEvent: p.BaseDendrite.Cfg.Global.JetStream.Prefixed(jetstream.OutputPresenceEvent),
|
||||
TopicDeviceListUpdate: p.BaseDendrite.Cfg.Global.JetStream.Prefixed(jetstream.InputDeviceListUpdate),
|
||||
TopicSigningKeyUpdate: p.BaseDendrite.Cfg.Global.JetStream.Prefixed(jetstream.InputSigningKeyUpdate),
|
||||
Config: &p.BaseDendrite.Cfg.FederationAPI,
|
||||
UserAPI: userAPI,
|
||||
}
|
||||
relayAPI := relayapi.NewRelayInternalAPI(p.BaseDendrite, federation, rsAPI, keyRing, producer, enableRelaying)
|
||||
logrus.Infof("Relaying enabled: %v", relayAPI.RelayingEnabled())
|
||||
|
||||
p.dendrite = setup.Monolith{
|
||||
Config: p.BaseDendrite.Cfg,
|
||||
Client: conn.CreateClient(p.BaseDendrite, p.Sessions),
|
||||
FedClient: federation,
|
||||
KeyRing: keyRing,
|
||||
|
||||
AppserviceAPI: asAPI,
|
||||
FederationAPI: fsAPI,
|
||||
RoomserverAPI: rsAPI,
|
||||
UserAPI: userAPI,
|
||||
RelayAPI: relayAPI,
|
||||
ExtPublicRoomsProvider: roomProvider,
|
||||
ExtUserDirectoryProvider: userProvider,
|
||||
}
|
||||
p.dendrite.AddAllPublicRoutes(p.BaseDendrite)
|
||||
|
||||
p.setupHttpServers(userProvider, enableWebsockets)
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) GetFederationAPI() federationAPI.FederationInternalAPI {
|
||||
return p.dendrite.FederationAPI
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) GetRelayAPI() relayAPI.RelayInternalAPI {
|
||||
return p.dendrite.RelayAPI
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) GetUserAPI() userAPI.UserInternalAPI {
|
||||
return p.dendrite.UserAPI
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) StartMonolith() {
|
||||
p.startHTTPServers()
|
||||
p.startEventHandler()
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) Stop() {
|
||||
logrus.Info("Stopping monolith")
|
||||
_ = p.BaseDendrite.Close()
|
||||
p.WaitForShutdown()
|
||||
logrus.Info("Stopped monolith")
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) WaitForShutdown() {
|
||||
p.BaseDendrite.WaitForShutdown()
|
||||
p.closeAllResources()
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) closeAllResources() {
|
||||
logrus.Info("Closing monolith resources")
|
||||
if p.httpServer != nil {
|
||||
_ = p.httpServer.Shutdown(context.Background())
|
||||
}
|
||||
|
||||
select {
|
||||
case p.stopHandlingEvents <- true:
|
||||
default:
|
||||
}
|
||||
|
||||
if p.listener != nil {
|
||||
_ = p.listener.Close()
|
||||
}
|
||||
|
||||
if p.Multicast != nil {
|
||||
p.Multicast.Stop()
|
||||
}
|
||||
|
||||
if p.Sessions != nil {
|
||||
_ = p.Sessions.Close()
|
||||
}
|
||||
|
||||
if p.Router != nil {
|
||||
_ = p.Router.Close()
|
||||
}
|
||||
logrus.Info("Monolith resources closed")
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) Addr() string {
|
||||
return p.httpListenAddr
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) setupHttpServers(userProvider *users.PineconeUserProvider, enableWebsockets bool) {
|
||||
p.httpMux = mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||
p.httpMux.PathPrefix(httputil.PublicClientPathPrefix).Handler(p.BaseDendrite.PublicClientAPIMux)
|
||||
p.httpMux.PathPrefix(httputil.PublicMediaPathPrefix).Handler(p.BaseDendrite.PublicMediaAPIMux)
|
||||
p.httpMux.PathPrefix(httputil.DendriteAdminPathPrefix).Handler(p.BaseDendrite.DendriteAdminMux)
|
||||
p.httpMux.PathPrefix(httputil.SynapseAdminPathPrefix).Handler(p.BaseDendrite.SynapseAdminMux)
|
||||
|
||||
if enableWebsockets {
|
||||
wsUpgrader := websocket.Upgrader{
|
||||
CheckOrigin: func(_ *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
p.httpMux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := wsUpgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Failed to upgrade WebSocket connection")
|
||||
return
|
||||
}
|
||||
conn := conn.WrapWebSocketConn(c)
|
||||
if _, err = p.Router.Connect(
|
||||
conn,
|
||||
pineconeRouter.ConnectionZone("websocket"),
|
||||
pineconeRouter.ConnectionPeerType(pineconeRouter.PeerTypeRemote),
|
||||
); err != nil {
|
||||
logrus.WithError(err).Error("Failed to connect WebSocket peer to Pinecone switch")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
p.httpMux.HandleFunc("/pinecone", p.Router.ManholeHandler)
|
||||
|
||||
if enableWebsockets {
|
||||
embed.Embed(p.httpMux, p.port, "Pinecone Demo")
|
||||
}
|
||||
|
||||
p.pineconeMux = mux.NewRouter().SkipClean(true).UseEncodedPath()
|
||||
p.pineconeMux.PathPrefix(users.PublicURL).HandlerFunc(userProvider.FederatedUserProfiles)
|
||||
p.pineconeMux.PathPrefix(httputil.PublicFederationPathPrefix).Handler(p.BaseDendrite.PublicFederationAPIMux)
|
||||
p.pineconeMux.PathPrefix(httputil.PublicMediaPathPrefix).Handler(p.BaseDendrite.PublicMediaAPIMux)
|
||||
|
||||
pHTTP := p.Sessions.Protocol(SessionProtocol).HTTP()
|
||||
pHTTP.Mux().Handle(users.PublicURL, p.pineconeMux)
|
||||
pHTTP.Mux().Handle(httputil.PublicFederationPathPrefix, p.pineconeMux)
|
||||
pHTTP.Mux().Handle(httputil.PublicMediaPathPrefix, p.pineconeMux)
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) startHTTPServers() {
|
||||
go func() {
|
||||
// Build both ends of a HTTP multiplex.
|
||||
p.httpServer = &http.Server{
|
||||
Addr: ":0",
|
||||
TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
IdleTimeout: 30 * time.Second,
|
||||
BaseContext: func(_ net.Listener) context.Context {
|
||||
return context.Background()
|
||||
},
|
||||
Handler: p.pineconeMux,
|
||||
}
|
||||
|
||||
pubkey := p.Router.PublicKey()
|
||||
pubkeyString := hex.EncodeToString(pubkey[:])
|
||||
logrus.Info("Listening on ", pubkeyString)
|
||||
|
||||
switch p.httpServer.Serve(p.Sessions.Protocol(SessionProtocol)) {
|
||||
case net.ErrClosed, http.ErrServerClosed:
|
||||
logrus.Info("Stopped listening on ", pubkeyString)
|
||||
default:
|
||||
logrus.Error("Stopped listening on ", pubkeyString)
|
||||
}
|
||||
logrus.Info("Stopped goroutine listening on ", pubkeyString)
|
||||
}()
|
||||
|
||||
p.httpListenAddr = fmt.Sprintf(":%d", p.port)
|
||||
go func() {
|
||||
logrus.Info("Listening on ", p.httpListenAddr)
|
||||
switch http.ListenAndServe(p.httpListenAddr, p.httpMux) {
|
||||
case net.ErrClosed, http.ErrServerClosed:
|
||||
logrus.Info("Stopped listening on ", p.httpListenAddr)
|
||||
default:
|
||||
logrus.Error("Stopped listening on ", p.httpListenAddr)
|
||||
}
|
||||
logrus.Info("Stopped goroutine listening on ", p.httpListenAddr)
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *P2PMonolith) startEventHandler() {
|
||||
p.stopHandlingEvents = make(chan bool)
|
||||
stopRelayServerSync := make(chan bool)
|
||||
eLog := logrus.WithField("pinecone", "events")
|
||||
p.RelayRetriever = relay.NewRelayServerRetriever(
|
||||
context.Background(),
|
||||
gomatrixserverlib.ServerName(p.Router.PublicKey().String()),
|
||||
p.dendrite.FederationAPI,
|
||||
p.dendrite.RelayAPI,
|
||||
stopRelayServerSync,
|
||||
)
|
||||
p.RelayRetriever.InitializeRelayServers(eLog)
|
||||
|
||||
go func(ch <-chan pineconeEvents.Event) {
|
||||
for {
|
||||
select {
|
||||
case event := <-ch:
|
||||
switch e := event.(type) {
|
||||
case pineconeEvents.PeerAdded:
|
||||
p.RelayRetriever.StartSync()
|
||||
case pineconeEvents.PeerRemoved:
|
||||
if p.RelayRetriever.IsRunning() && p.Router.TotalPeerCount() == 0 {
|
||||
// NOTE: Don't block on channel
|
||||
select {
|
||||
case stopRelayServerSync <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
case pineconeEvents.BroadcastReceived:
|
||||
// eLog.Info("Broadcast received from: ", e.PeerID)
|
||||
|
||||
req := &federationAPI.PerformWakeupServersRequest{
|
||||
ServerNames: []gomatrixserverlib.ServerName{gomatrixserverlib.ServerName(e.PeerID)},
|
||||
}
|
||||
res := &federationAPI.PerformWakeupServersResponse{}
|
||||
if err := p.dendrite.FederationAPI.PerformWakeupServers(p.BaseDendrite.Context(), req, res); err != nil {
|
||||
eLog.WithError(err).Error("Failed to wakeup destination", e.PeerID)
|
||||
}
|
||||
}
|
||||
case <-p.stopHandlingEvents:
|
||||
logrus.Info("Stopping processing pinecone events")
|
||||
// NOTE: Don't block on channel
|
||||
select {
|
||||
case stopRelayServerSync <- true:
|
||||
default:
|
||||
}
|
||||
logrus.Info("Stopped processing pinecone events")
|
||||
return
|
||||
}
|
||||
}
|
||||
}(p.EventChannel)
|
||||
}
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package relay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
relayServerAPI "github.com/matrix-org/dendrite/relayapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
relayServerRetryInterval = time.Second * 30
|
||||
)
|
||||
|
||||
type RelayServerRetriever struct {
|
||||
ctx context.Context
|
||||
serverName gomatrixserverlib.ServerName
|
||||
federationAPI federationAPI.FederationInternalAPI
|
||||
relayAPI relayServerAPI.RelayInternalAPI
|
||||
relayServersQueried map[gomatrixserverlib.ServerName]bool
|
||||
queriedServersMutex sync.Mutex
|
||||
running atomic.Bool
|
||||
quit chan bool
|
||||
}
|
||||
|
||||
func NewRelayServerRetriever(
|
||||
ctx context.Context,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
federationAPI federationAPI.FederationInternalAPI,
|
||||
relayAPI relayServerAPI.RelayInternalAPI,
|
||||
quit chan bool,
|
||||
) RelayServerRetriever {
|
||||
return RelayServerRetriever{
|
||||
ctx: ctx,
|
||||
serverName: serverName,
|
||||
federationAPI: federationAPI,
|
||||
relayAPI: relayAPI,
|
||||
relayServersQueried: make(map[gomatrixserverlib.ServerName]bool),
|
||||
running: *atomic.NewBool(false),
|
||||
quit: quit,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RelayServerRetriever) InitializeRelayServers(eLog *logrus.Entry) {
|
||||
request := federationAPI.P2PQueryRelayServersRequest{Server: gomatrixserverlib.ServerName(r.serverName)}
|
||||
response := federationAPI.P2PQueryRelayServersResponse{}
|
||||
err := r.federationAPI.P2PQueryRelayServers(r.ctx, &request, &response)
|
||||
if err != nil {
|
||||
eLog.Warnf("Failed obtaining list of this node's relay servers: %s", err.Error())
|
||||
}
|
||||
|
||||
r.queriedServersMutex.Lock()
|
||||
defer r.queriedServersMutex.Unlock()
|
||||
for _, server := range response.RelayServers {
|
||||
r.relayServersQueried[server] = false
|
||||
}
|
||||
|
||||
eLog.Infof("Registered relay servers: %v", response.RelayServers)
|
||||
}
|
||||
|
||||
func (r *RelayServerRetriever) SetRelayServers(servers []gomatrixserverlib.ServerName) {
|
||||
UpdateNodeRelayServers(r.serverName, servers, r.ctx, r.federationAPI)
|
||||
|
||||
// Replace list of servers to sync with and mark them all as unsynced.
|
||||
r.queriedServersMutex.Lock()
|
||||
defer r.queriedServersMutex.Unlock()
|
||||
r.relayServersQueried = make(map[gomatrixserverlib.ServerName]bool)
|
||||
for _, server := range servers {
|
||||
r.relayServersQueried[server] = false
|
||||
}
|
||||
|
||||
r.StartSync()
|
||||
}
|
||||
|
||||
func (r *RelayServerRetriever) GetRelayServers() []gomatrixserverlib.ServerName {
|
||||
r.queriedServersMutex.Lock()
|
||||
defer r.queriedServersMutex.Unlock()
|
||||
relayServers := []gomatrixserverlib.ServerName{}
|
||||
for server := range r.relayServersQueried {
|
||||
relayServers = append(relayServers, server)
|
||||
}
|
||||
|
||||
return relayServers
|
||||
}
|
||||
|
||||
func (r *RelayServerRetriever) GetQueriedServerStatus() map[gomatrixserverlib.ServerName]bool {
|
||||
r.queriedServersMutex.Lock()
|
||||
defer r.queriedServersMutex.Unlock()
|
||||
|
||||
result := map[gomatrixserverlib.ServerName]bool{}
|
||||
for server, queried := range r.relayServersQueried {
|
||||
result[server] = queried
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *RelayServerRetriever) StartSync() {
|
||||
if !r.running.Load() {
|
||||
logrus.Info("Starting relay server sync")
|
||||
go r.SyncRelayServers(r.quit)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RelayServerRetriever) IsRunning() bool {
|
||||
return r.running.Load()
|
||||
}
|
||||
|
||||
func (r *RelayServerRetriever) SyncRelayServers(stop <-chan bool) {
|
||||
defer r.running.Store(false)
|
||||
|
||||
t := time.NewTimer(relayServerRetryInterval)
|
||||
for {
|
||||
relayServersToQuery := []gomatrixserverlib.ServerName{}
|
||||
func() {
|
||||
r.queriedServersMutex.Lock()
|
||||
defer r.queriedServersMutex.Unlock()
|
||||
for server, complete := range r.relayServersQueried {
|
||||
if !complete {
|
||||
relayServersToQuery = append(relayServersToQuery, server)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if len(relayServersToQuery) == 0 {
|
||||
// All relay servers have been synced.
|
||||
logrus.Info("Finished syncing with all known relays")
|
||||
return
|
||||
}
|
||||
r.queryRelayServers(relayServersToQuery)
|
||||
t.Reset(relayServerRetryInterval)
|
||||
|
||||
select {
|
||||
case <-stop:
|
||||
if !t.Stop() {
|
||||
<-t.C
|
||||
}
|
||||
logrus.Info("Stopped relay server retriever")
|
||||
return
|
||||
case <-t.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RelayServerRetriever) queryRelayServers(relayServers []gomatrixserverlib.ServerName) {
|
||||
logrus.Info("Querying relay servers for any available transactions")
|
||||
for _, server := range relayServers {
|
||||
userID, err := gomatrixserverlib.NewUserID("@user:"+string(r.serverName), false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Infof("Syncing with relay: %s", string(server))
|
||||
err = r.relayAPI.PerformRelayServerSync(context.Background(), *userID, server)
|
||||
if err == nil {
|
||||
func() {
|
||||
r.queriedServersMutex.Lock()
|
||||
defer r.queriedServersMutex.Unlock()
|
||||
r.relayServersQueried[server] = true
|
||||
}()
|
||||
// TODO : What happens if your relay receives new messages after this point?
|
||||
// Should you continue to check with them, or should they try and contact you?
|
||||
// They could send a "new_async_events" message your way maybe?
|
||||
// Then you could mark them as needing to be queried again.
|
||||
// What if you miss this message?
|
||||
// Maybe you should try querying them again after a certain period of time as a backup?
|
||||
} else {
|
||||
logrus.Errorf("Failed querying relay server: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateNodeRelayServers(
|
||||
node gomatrixserverlib.ServerName,
|
||||
relays []gomatrixserverlib.ServerName,
|
||||
ctx context.Context,
|
||||
fedAPI federationAPI.FederationInternalAPI,
|
||||
) {
|
||||
// Get the current relay list
|
||||
request := federationAPI.P2PQueryRelayServersRequest{Server: node}
|
||||
response := federationAPI.P2PQueryRelayServersResponse{}
|
||||
err := fedAPI.P2PQueryRelayServers(ctx, &request, &response)
|
||||
if err != nil {
|
||||
logrus.Warnf("Failed obtaining list of relay servers for %s: %s", node, err.Error())
|
||||
}
|
||||
|
||||
// Remove old, non-matching relays
|
||||
var serversToRemove []gomatrixserverlib.ServerName
|
||||
for _, existingServer := range response.RelayServers {
|
||||
shouldRemove := true
|
||||
for _, newServer := range relays {
|
||||
if newServer == existingServer {
|
||||
shouldRemove = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if shouldRemove {
|
||||
serversToRemove = append(serversToRemove, existingServer)
|
||||
}
|
||||
}
|
||||
removeRequest := federationAPI.P2PRemoveRelayServersRequest{
|
||||
Server: node,
|
||||
RelayServers: serversToRemove,
|
||||
}
|
||||
removeResponse := federationAPI.P2PRemoveRelayServersResponse{}
|
||||
err = fedAPI.P2PRemoveRelayServers(ctx, &removeRequest, &removeResponse)
|
||||
if err != nil {
|
||||
logrus.Warnf("Failed removing old relay servers for %s: %s", node, err.Error())
|
||||
}
|
||||
|
||||
// Add new relays
|
||||
addRequest := federationAPI.P2PAddRelayServersRequest{
|
||||
Server: node,
|
||||
RelayServers: relays,
|
||||
}
|
||||
addResponse := federationAPI.P2PAddRelayServersResponse{}
|
||||
err = fedAPI.P2PAddRelayServers(ctx, &addRequest, &addResponse)
|
||||
if err != nil {
|
||||
logrus.Warnf("Failed adding relay servers for %s: %s", node, err.Error())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package relay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
relayServerAPI "github.com/matrix-org/dendrite/relayapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gotest.tools/v3/poll"
|
||||
)
|
||||
|
||||
var testRelayServers = []gomatrixserverlib.ServerName{"relay1", "relay2"}
|
||||
|
||||
type FakeFedAPI struct {
|
||||
federationAPI.FederationInternalAPI
|
||||
}
|
||||
|
||||
func (f *FakeFedAPI) P2PQueryRelayServers(
|
||||
ctx context.Context,
|
||||
req *federationAPI.P2PQueryRelayServersRequest,
|
||||
res *federationAPI.P2PQueryRelayServersResponse,
|
||||
) error {
|
||||
res.RelayServers = testRelayServers
|
||||
return nil
|
||||
}
|
||||
|
||||
type FakeRelayAPI struct {
|
||||
relayServerAPI.RelayInternalAPI
|
||||
}
|
||||
|
||||
func (r *FakeRelayAPI) PerformRelayServerSync(
|
||||
ctx context.Context,
|
||||
userID gomatrixserverlib.UserID,
|
||||
relayServer gomatrixserverlib.ServerName,
|
||||
) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRelayRetrieverInitialization(t *testing.T) {
|
||||
retriever := NewRelayServerRetriever(
|
||||
context.Background(),
|
||||
"server",
|
||||
&FakeFedAPI{},
|
||||
&FakeRelayAPI{},
|
||||
make(chan bool),
|
||||
)
|
||||
|
||||
retriever.InitializeRelayServers(logrus.WithField("test", "relay"))
|
||||
relayServers := retriever.GetQueriedServerStatus()
|
||||
assert.Equal(t, 2, len(relayServers))
|
||||
}
|
||||
|
||||
func TestRelayRetrieverSync(t *testing.T) {
|
||||
retriever := NewRelayServerRetriever(
|
||||
context.Background(),
|
||||
"server",
|
||||
&FakeFedAPI{},
|
||||
&FakeRelayAPI{},
|
||||
make(chan bool),
|
||||
)
|
||||
|
||||
retriever.InitializeRelayServers(logrus.WithField("test", "relay"))
|
||||
relayServers := retriever.GetQueriedServerStatus()
|
||||
assert.Equal(t, 2, len(relayServers))
|
||||
|
||||
stopRelayServerSync := make(chan bool)
|
||||
go retriever.SyncRelayServers(stopRelayServerSync)
|
||||
|
||||
check := func(log poll.LogT) poll.Result {
|
||||
relayServers := retriever.GetQueriedServerStatus()
|
||||
for _, queried := range relayServers {
|
||||
if !queried {
|
||||
return poll.Continue("waiting for all servers to be queried")
|
||||
}
|
||||
}
|
||||
|
||||
stopRelayServerSync <- true
|
||||
return poll.Success()
|
||||
}
|
||||
poll.WaitOn(t, check, poll.WithTimeout(5*time.Second), poll.WithDelay(100*time.Millisecond))
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rooms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/defaults"
|
||||
"github.com/matrix-org/dendrite/federationapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
|
||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||
)
|
||||
|
||||
type PineconeRoomProvider struct {
|
||||
r *pineconeRouter.Router
|
||||
s *pineconeSessions.Sessions
|
||||
fedSender api.FederationInternalAPI
|
||||
fedClient *gomatrixserverlib.FederationClient
|
||||
}
|
||||
|
||||
func NewPineconeRoomProvider(
|
||||
r *pineconeRouter.Router,
|
||||
s *pineconeSessions.Sessions,
|
||||
fedSender api.FederationInternalAPI,
|
||||
fedClient *gomatrixserverlib.FederationClient,
|
||||
) *PineconeRoomProvider {
|
||||
p := &PineconeRoomProvider{
|
||||
r: r,
|
||||
s: s,
|
||||
fedSender: fedSender,
|
||||
fedClient: fedClient,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *PineconeRoomProvider) Rooms() []gomatrixserverlib.PublicRoom {
|
||||
list := map[gomatrixserverlib.ServerName]struct{}{}
|
||||
for k := range defaults.DefaultServerNames {
|
||||
list[k] = struct{}{}
|
||||
}
|
||||
for _, k := range p.r.Peers() {
|
||||
list[gomatrixserverlib.ServerName(k.PublicKey)] = struct{}{}
|
||||
}
|
||||
return bulkFetchPublicRoomsFromServers(
|
||||
context.Background(), p.fedClient,
|
||||
gomatrixserverlib.ServerName(p.r.PublicKey().String()), list,
|
||||
)
|
||||
}
|
||||
|
||||
// bulkFetchPublicRoomsFromServers fetches public rooms from the list of homeservers.
|
||||
// Returns a list of public rooms.
|
||||
func bulkFetchPublicRoomsFromServers(
|
||||
ctx context.Context, fedClient *gomatrixserverlib.FederationClient,
|
||||
origin gomatrixserverlib.ServerName,
|
||||
homeservers map[gomatrixserverlib.ServerName]struct{},
|
||||
) (publicRooms []gomatrixserverlib.PublicRoom) {
|
||||
limit := 200
|
||||
// follow pipeline semantics, see https://blog.golang.org/pipelines for more info.
|
||||
// goroutines send rooms to this channel
|
||||
roomCh := make(chan gomatrixserverlib.PublicRoom, int(limit))
|
||||
// signalling channel to tell goroutines to stop sending rooms and quit
|
||||
done := make(chan bool)
|
||||
// signalling to say when we can close the room channel
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(homeservers))
|
||||
// concurrently query for public rooms
|
||||
reqctx, reqcancel := context.WithTimeout(ctx, time.Second*5)
|
||||
for hs := range homeservers {
|
||||
go func(homeserverDomain gomatrixserverlib.ServerName) {
|
||||
defer wg.Done()
|
||||
util.GetLogger(reqctx).WithField("hs", homeserverDomain).Info("Querying HS for public rooms")
|
||||
fres, err := fedClient.GetPublicRooms(reqctx, origin, homeserverDomain, int(limit), "", false, "")
|
||||
if err != nil {
|
||||
util.GetLogger(reqctx).WithError(err).WithField("hs", homeserverDomain).Warn(
|
||||
"bulkFetchPublicRoomsFromServers: failed to query hs",
|
||||
)
|
||||
return
|
||||
}
|
||||
for _, room := range fres.Chunk {
|
||||
// atomically send a room or stop
|
||||
select {
|
||||
case roomCh <- room:
|
||||
case <-done:
|
||||
case <-reqctx.Done():
|
||||
util.GetLogger(reqctx).WithError(err).WithField("hs", homeserverDomain).Info("Interrupted whilst sending rooms")
|
||||
return
|
||||
}
|
||||
}
|
||||
}(hs)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
default:
|
||||
wg.Wait()
|
||||
}
|
||||
reqcancel()
|
||||
close(done)
|
||||
close(roomCh)
|
||||
|
||||
for room := range roomCh {
|
||||
publicRooms = append(publicRooms, room)
|
||||
}
|
||||
|
||||
return publicRooms
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package users
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
clienthttputil "github.com/matrix-org/dendrite/clientapi/httputil"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-pinecone/defaults"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
|
||||
pineconeRouter "github.com/matrix-org/pinecone/router"
|
||||
pineconeSessions "github.com/matrix-org/pinecone/sessions"
|
||||
)
|
||||
|
||||
type PineconeUserProvider struct {
|
||||
r *pineconeRouter.Router
|
||||
s *pineconeSessions.Sessions
|
||||
userAPI userapi.QuerySearchProfilesAPI
|
||||
fedClient *gomatrixserverlib.FederationClient
|
||||
}
|
||||
|
||||
const PublicURL = "/_matrix/p2p/profiles"
|
||||
|
||||
func NewPineconeUserProvider(
|
||||
r *pineconeRouter.Router,
|
||||
s *pineconeSessions.Sessions,
|
||||
userAPI userapi.QuerySearchProfilesAPI,
|
||||
fedClient *gomatrixserverlib.FederationClient,
|
||||
) *PineconeUserProvider {
|
||||
p := &PineconeUserProvider{
|
||||
r: r,
|
||||
s: s,
|
||||
userAPI: userAPI,
|
||||
fedClient: fedClient,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *PineconeUserProvider) FederatedUserProfiles(w http.ResponseWriter, r *http.Request) {
|
||||
req := &userapi.QuerySearchProfilesRequest{Limit: 25}
|
||||
res := &userapi.QuerySearchProfilesResponse{}
|
||||
if err := clienthttputil.UnmarshalJSONRequest(r, &req); err != nil {
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
if err := p.userAPI.QuerySearchProfiles(r.Context(), req, res); err != nil {
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
j, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(200)
|
||||
_, _ = w.Write(j)
|
||||
}
|
||||
|
||||
func (p *PineconeUserProvider) QuerySearchProfiles(ctx context.Context, req *userapi.QuerySearchProfilesRequest, res *userapi.QuerySearchProfilesResponse) error {
|
||||
list := map[gomatrixserverlib.ServerName]struct{}{}
|
||||
for k := range defaults.DefaultServerNames {
|
||||
list[k] = struct{}{}
|
||||
}
|
||||
for _, k := range p.r.Peers() {
|
||||
list[gomatrixserverlib.ServerName(k.PublicKey)] = struct{}{}
|
||||
}
|
||||
res.Profiles = bulkFetchUserDirectoriesFromServers(context.Background(), req, p.fedClient, list)
|
||||
return nil
|
||||
}
|
||||
|
||||
// bulkFetchUserDirectoriesFromServers fetches users from the list of homeservers.
|
||||
// Returns a list of user profiles.
|
||||
func bulkFetchUserDirectoriesFromServers(
|
||||
ctx context.Context, req *userapi.QuerySearchProfilesRequest,
|
||||
fedClient *gomatrixserverlib.FederationClient,
|
||||
homeservers map[gomatrixserverlib.ServerName]struct{},
|
||||
) (profiles []authtypes.Profile) {
|
||||
jsonBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
limit := 200
|
||||
// follow pipeline semantics, see https://blog.golang.org/pipelines for more info.
|
||||
// goroutines send rooms to this channel
|
||||
profileCh := make(chan authtypes.Profile, int(limit))
|
||||
// signalling channel to tell goroutines to stop sending rooms and quit
|
||||
done := make(chan bool)
|
||||
// signalling to say when we can close the room channel
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(homeservers))
|
||||
// concurrently query for public rooms
|
||||
reqctx, reqcancel := context.WithTimeout(ctx, time.Second*5)
|
||||
for hs := range homeservers {
|
||||
go func(homeserverDomain gomatrixserverlib.ServerName) {
|
||||
defer wg.Done()
|
||||
util.GetLogger(reqctx).WithField("hs", homeserverDomain).Info("Querying HS for users")
|
||||
|
||||
jsonBodyReader := bytes.NewBuffer(jsonBody)
|
||||
httpReq, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("matrix://%s%s", homeserverDomain, PublicURL), jsonBodyReader)
|
||||
if err != nil {
|
||||
util.GetLogger(reqctx).WithError(err).WithField("hs", homeserverDomain).Warn(
|
||||
"bulkFetchUserDirectoriesFromServers: failed to create request",
|
||||
)
|
||||
}
|
||||
res := &userapi.QuerySearchProfilesResponse{}
|
||||
if err = fedClient.DoRequestAndParseResponse(reqctx, httpReq, res); err != nil {
|
||||
util.GetLogger(reqctx).WithError(err).WithField("hs", homeserverDomain).Warn(
|
||||
"bulkFetchUserDirectoriesFromServers: failed to query hs",
|
||||
)
|
||||
return
|
||||
}
|
||||
for _, profile := range res.Profiles {
|
||||
profile.ServerName = string(homeserverDomain)
|
||||
// atomically send a room or stop
|
||||
select {
|
||||
case profileCh <- profile:
|
||||
case <-done:
|
||||
case <-reqctx.Done():
|
||||
util.GetLogger(reqctx).WithError(err).WithField("hs", homeserverDomain).Info("Interrupted whilst sending profiles")
|
||||
return
|
||||
}
|
||||
}
|
||||
}(hs)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
default:
|
||||
wg.Wait()
|
||||
}
|
||||
reqcancel()
|
||||
close(done)
|
||||
close(profileCh)
|
||||
|
||||
for profile := range profileCh {
|
||||
profiles = append(profiles, profile)
|
||||
}
|
||||
|
||||
return profiles
|
||||
}
|
||||
8
cmd/dendrite/Dockerfile.dev
Normal file
8
cmd/dendrite/Dockerfile.dev
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
FROM alpine:latest
|
||||
|
||||
COPY dendrite-monolith-server /usr/bin/
|
||||
|
||||
VOLUME /etc/dendrite
|
||||
WORKDIR /etc/dendrite
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dendrite"]
|
||||
12
cmd/dendrite/build_dev.sh
Normal file
12
cmd/dendrite/build_dev.sh
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
set -xe
|
||||
if [ -z "$(git status --porcelain)" ]; then
|
||||
CGO_ENABLED=0 go build .
|
||||
TAG=$(git rev-parse --short HEAD)
|
||||
docker build -f Dockerfile.dev -t gcr.io/globekeeper-development/dendrite-monolith:$TAG -t gcr.io/globekeeper-development/dendrite-monolith -t gcr.io/globekeeper-production/dendrite-monolith:$TAG .
|
||||
docker push gcr.io/globekeeper-development/dendrite-monolith:$TAG
|
||||
docker push gcr.io/globekeeper-production/dendrite-monolith:$TAG
|
||||
docker push gcr.io/globekeeper-development/dendrite-monolith
|
||||
else
|
||||
echo "Please commit changes"
|
||||
exit 0
|
||||
fi
|
||||
|
|
@ -42,7 +42,6 @@ func main() {
|
|||
"roomserver": &cfg.RoomServer.Database,
|
||||
"syncapi": &cfg.SyncAPI.Database,
|
||||
"userapi": &cfg.UserAPI.AccountDatabase,
|
||||
"relayapi": &cfg.RelayAPI.Database,
|
||||
} {
|
||||
if uri == "" {
|
||||
path := filepath.Join(*dirPath, fmt.Sprintf("dendrite_%s.db", name))
|
||||
|
|
|
|||
13
go.mod
13
go.mod
|
|
@ -16,14 +16,12 @@ require (
|
|||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/kardianos/minwinsvc v1.0.2
|
||||
github.com/lib/pq v1.10.7
|
||||
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/gomatrix v0.0.0-20220926102614-ceba4d9f7530
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20230131183213-122f1e0e3fa1
|
||||
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/nats-io/nats-server/v2 v2.9.15
|
||||
|
|
@ -51,7 +49,6 @@ require (
|
|||
gopkg.in/yaml.v2 v2.4.0
|
||||
gotest.tools/v3 v3.4.0
|
||||
modernc.org/sqlite v1.19.3
|
||||
nhooyr.io/websocket v1.8.7
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -85,14 +82,11 @@ require (
|
|||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gogo/protobuf v1.1.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/h2non/filetype v1.1.3 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/juju/errors v1.0.0 // indirect
|
||||
|
|
@ -110,23 +104,16 @@ require (
|
|||
github.com/nats-io/jwt/v2 v2.3.0 // indirect
|
||||
github.com/nats-io/nkeys v0.3.0 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.3.0 // indirect
|
||||
github.com/onsi/gomega v1.22.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||
github.com/quic-go/quic-go v0.32.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
|
|
|
|||
66
go.sum
66
go.sum
|
|
@ -153,10 +153,6 @@ github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBav
|
|||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70=
|
||||
github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
|
|
@ -171,23 +167,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
|
||||
|
|
@ -208,8 +188,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
|||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
|
@ -256,8 +234,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
|||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
|
@ -266,9 +242,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
|||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
|
|
@ -277,10 +250,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
|||
github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk=
|
||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
|
|
@ -298,7 +269,6 @@ github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3M
|
|||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
|
||||
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
|
@ -311,8 +281,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e h1:DP5RC0Z3XdyBEW5dKt8YPeN6vZbm6OzVaGVp7f1BQRM=
|
||||
|
|
@ -323,13 +291,10 @@ github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8
|
|||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20230131183213-122f1e0e3fa1 h1:JSw0nmjMrgBmoM2aQsa78LTpI5BnuD9+vOiEQ4Qo0qw=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20230131183213-122f1e0e3fa1/go.mod h1:Mtifyr8q8htcBeugvlDnkBcNUy5LO8OzUoplAf1+mb4=
|
||||
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a h1:awrPDf9LEFySxTLKYBMCiObelNx/cBuv/wzllvCCH3A=
|
||||
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a/go.mod h1:HchJX9oKMXaT2xYFs0Ha/6Zs06mxLU8k6F1ODnrGkeQ=
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y=
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66/go.mod h1:iBI1foelCqA09JJgPV0FYz4qA5dUXYOxMi57FxKBdd4=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
|
|
@ -373,10 +338,6 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S
|
|||
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79 h1:Dmx8g2747UTVPzSkmohk84S3g/uWqd6+f4SSLPhLcfA=
|
||||
github.com/ngrok/sqlmw v0.0.0-20220520173518-97c9c04efc79/go.mod h1:E26fwEtRNigBfFfHDWsklmo0T7Ixbg0XXgck+Hq4O9k=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.3.0 h1:kUMoxMoQG3ogk/QWyKh3zibV7BKZ+xBpWil1cTylVqc=
|
||||
github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=
|
||||
|
|
@ -385,7 +346,6 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+
|
|||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
|
|
@ -420,14 +380,6 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
|||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
|
||||
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa h1:tEkEyxYeZ43TR55QU/hsIt9aRGBxbgGuz9CGykjvogY=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
|
|
@ -453,7 +405,6 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
|
|
@ -474,10 +425,6 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO
|
|||
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.4.6 h1:GALUDV9QPz/5FVkbazpkTc9EABHufA556JwUJZr41j4=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.4.6/go.mod h1:PBMoAOvQjA9geNEeGyMXA9QgCS6Bu+9V+1VkWM84wpw=
|
||||
|
|
@ -485,7 +432,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
|
|
@ -521,8 +467,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
|
@ -548,7 +492,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
|
|
@ -582,7 +525,6 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
|
|
@ -606,7 +548,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -627,7 +568,6 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -647,9 +587,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -724,7 +662,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
|||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
|
|
@ -829,7 +766,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
|
@ -870,8 +806,6 @@ modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
|
|||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
|
|
|||
|
|
@ -1,134 +0,0 @@
|
|||
## Relay Server Architecture
|
||||
|
||||
Relay Servers function similar to the way physical mail drop boxes do.
|
||||
A node can have many associated relay servers. Matrix events can be sent to them instead of to the destination node, and the destination node will eventually retrieve them from the relay server.
|
||||
Nodes that want to send events to an offline node need to know what relay servers are associated with their intended destination.
|
||||
Currently this is manually configured in the dendrite database. In the future this information could be configurable in the app and shared automatically via other means.
|
||||
|
||||
Currently events are sent as complete Matrix Transactions.
|
||||
Transactions include a list of PDUs, (which contain, among other things, lists of authorization events, previous events, and signatures) a list of EDUs, and other information about the transaction.
|
||||
There is no additional information sent along with the transaction other than what is typically added to them during Matrix federation today.
|
||||
In the future this will probably need to change in order to handle more complex room state resolution during p2p usage.
|
||||
|
||||
### Design
|
||||
|
||||
```
|
||||
0 +--------------------+
|
||||
+----------------------------------------+ | P2P Node A |
|
||||
| Relay Server | | +--------+ |
|
||||
| | | | Client | |
|
||||
| +--------------------+ | | +--------+ |
|
||||
| | Relay Server API | | | | |
|
||||
| | | | | V |
|
||||
| .--------. 2 | +-------------+ | | 1 | +------------+ |
|
||||
| |`--------`| <----- | Forwarder | <------------- | Homeserver | |
|
||||
| | Database | | +-------------+ | | | +------------+ |
|
||||
| `----------` | | | +--------------------+
|
||||
| ^ | | |
|
||||
| | 4 | +-------------+ | |
|
||||
| `------------ | Retriever | <------. +--------------------+
|
||||
| | +-------------+ | | | | P2P Node B |
|
||||
| | | | | | +--------+ |
|
||||
| +--------------------+ | | | | Client | |
|
||||
| | | | +--------+ |
|
||||
+----------------------------------------+ | | | |
|
||||
| | V |
|
||||
3 | | +------------+ |
|
||||
`------ | Homeserver | |
|
||||
| +------------+ |
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
- 0: This relay server is currently only acting on behalf of `P2P Node B`. It will only receive, and later forward events that are destined for `P2P Node B`.
|
||||
- 1: When `P2P Node A` fails sending directly to `P2P Node B` (after a configurable number of attempts), it checks for any known relay servers associated with `P2P Node B` and sends to all of them.
|
||||
- If sending to any of the relay servers succeeds, that transaction is considered to be successfully sent.
|
||||
- 2: The relay server `forwarder` stores the transaction json in its database and marks it as destined for `P2P Node B`.
|
||||
- 3: When `P2P Node B` comes online, it queries all its relay servers for any missed messages.
|
||||
- 4: The relay server `retriever` will look in its database for any transactions that are destined for `P2P Node B` and returns them one at a time.
|
||||
|
||||
For now, it is important that we don’t design out a hybrid approach of having both sender-side and recipient-side relay servers.
|
||||
Both approaches make sense and determining which makes for a better experience depends on the use case.
|
||||
|
||||
#### Sender-Side Relay Servers
|
||||
|
||||
If we are running around truly ad-hoc, and I don't know when or where you will be able to pick up messages, then having a sender designated server makes sense to give things the best chance at making their way to the destination.
|
||||
But in order to achieve this, you are either relying on p2p presence broadcasts for the relay to know when to try forwarding (which means you are in a pretty small network), or the relay just keeps on periodically attempting to forward to the destination which will lead to a lot of extra traffic on the network.
|
||||
|
||||
#### Recipient-Side Relay Servers
|
||||
|
||||
If we have agreed to some static relay server before going off and doing other things, or if we are talking about more global p2p federation, then having a recipient designated relay server can cut down on redundant traffic since it will sit there idle until the recipient pulls events from it.
|
||||
|
||||
### API
|
||||
|
||||
Relay servers make use of 2 new matrix federation endpoints.
|
||||
These are:
|
||||
- PUT /_matrix/federation/v1/send_relay/{txnID}/{userID}
|
||||
- GET /_matrix/federation/v1/relay_txn/{userID}
|
||||
|
||||
#### Send_Relay
|
||||
|
||||
The `send_relay` endpoint is used to send events to a relay server that are destined for some other node. Servers can send events to this endpoint if they wish for the relay server to store & forward events for them when they go offline.
|
||||
|
||||
##### Request
|
||||
|
||||
###### Request Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|--------|--------|-----------------------------------------------------|
|
||||
| txnID | string | **Required:** The transaction ID. |
|
||||
| userID | string | **Required:** The destination for this transaciton. |
|
||||
|
||||
###### Request Body
|
||||
|
||||
| Name | Type | Description |
|
||||
|--------|--------|----------------------------------------|
|
||||
| pdus | [PDU] | **Required:** List of pdus. Max 50. |
|
||||
| edus | [EDU] | List of edus. May be omitted. Max 100. |
|
||||
|
||||
##### Responses
|
||||
|
||||
| Code | Reason |
|
||||
|--------|--------------------------------------------------|
|
||||
| 200 | Successfully stored transaction for forwarding. |
|
||||
| 400 | Invalid userID. |
|
||||
| 400 | Invalid request body. |
|
||||
| 400 | Too many pdus or edus. |
|
||||
| 500 | Server failed processing transaction. |
|
||||
|
||||
#### Relay_Txn
|
||||
|
||||
The `relay_txn` endpoint is used to get events from a relay server that are destined for you. Servers can send events to this endpoint if they wish for the relay server to store & forward events for them when they go offline.
|
||||
|
||||
##### Request
|
||||
|
||||
**This needs to be changed to prevent nodes from obtaining transactions not destined for them. Possibly by adding a signature field to the request.**
|
||||
|
||||
###### Request Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|--------|--------|----------------------------------------------------------------|
|
||||
| userID | string | **Required:** The user ID that events are being requested for. |
|
||||
|
||||
###### Request Body
|
||||
|
||||
| Name | Type | Description |
|
||||
|----------|--------|----------------------------------------|
|
||||
| entry_id | int64 | **Required:** The id of the previous transaction received from the relay. Provided in the previous response to this endpoint. |
|
||||
|
||||
##### Responses
|
||||
|
||||
| Code | Reason |
|
||||
|--------|--------------------------------------------------|
|
||||
| 200 | Successfully stored transaction for forwarding. |
|
||||
| 400 | Invalid userID. |
|
||||
| 400 | Invalid request body. |
|
||||
| 400 | Invalid previous entry. Must be >= 0 |
|
||||
| 500 | Server failed processing transaction. |
|
||||
|
||||
###### 200 Response Body
|
||||
|
||||
| Name | Type | Description |
|
||||
|----------------|--------------|--------------------------------------------------------------------------|
|
||||
| transaction | Transaction | **Required:** A matrix transaction. |
|
||||
| entry_id | int64 | An ID associated with this transaction. |
|
||||
| entries_queued | bool | **Required:** Whether or not there are more events stored for this user. |
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// RelayInternalAPI is used to query information from the relay server.
|
||||
type RelayInternalAPI interface {
|
||||
RelayServerAPI
|
||||
|
||||
// Retrieve from external relay server all transactions stored for us and process them.
|
||||
PerformRelayServerSync(
|
||||
ctx context.Context,
|
||||
userID gomatrixserverlib.UserID,
|
||||
relayServer gomatrixserverlib.ServerName,
|
||||
) error
|
||||
|
||||
// Tells the relayapi whether or not it should act as a relay server for external servers.
|
||||
SetRelayingEnabled(bool)
|
||||
|
||||
// Obtain whether the relayapi is currently configured to act as a relay server for external servers.
|
||||
RelayingEnabled() bool
|
||||
}
|
||||
|
||||
// RelayServerAPI exposes the store & query transaction functionality of a relay server.
|
||||
type RelayServerAPI interface {
|
||||
// Store transactions for forwarding to the destination at a later time.
|
||||
PerformStoreTransaction(
|
||||
ctx context.Context,
|
||||
transaction gomatrixserverlib.Transaction,
|
||||
userID gomatrixserverlib.UserID,
|
||||
) error
|
||||
|
||||
// Obtain the oldest stored transaction for the specified userID.
|
||||
QueryTransactions(
|
||||
ctx context.Context,
|
||||
userID gomatrixserverlib.UserID,
|
||||
previousEntry gomatrixserverlib.RelayEntry,
|
||||
) (QueryRelayTransactionsResponse, error)
|
||||
}
|
||||
|
||||
type QueryRelayTransactionsResponse struct {
|
||||
Transaction gomatrixserverlib.Transaction `json:"transaction"`
|
||||
EntryID int64 `json:"entry_id"`
|
||||
EntriesQueued bool `json:"entries_queued"`
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
fedAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
"github.com/matrix-org/dendrite/federationapi/producers"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage"
|
||||
rsAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type RelayInternalAPI struct {
|
||||
db storage.Database
|
||||
fedClient fedAPI.FederationClient
|
||||
rsAPI rsAPI.RoomserverInternalAPI
|
||||
keyRing *gomatrixserverlib.KeyRing
|
||||
producer *producers.SyncAPIProducer
|
||||
presenceEnabledInbound bool
|
||||
serverName gomatrixserverlib.ServerName
|
||||
relayingEnabledMutex sync.Mutex
|
||||
relayingEnabled bool
|
||||
}
|
||||
|
||||
func NewRelayInternalAPI(
|
||||
db storage.Database,
|
||||
fedClient fedAPI.FederationClient,
|
||||
rsAPI rsAPI.RoomserverInternalAPI,
|
||||
keyRing *gomatrixserverlib.KeyRing,
|
||||
producer *producers.SyncAPIProducer,
|
||||
presenceEnabledInbound bool,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
relayingEnabled bool,
|
||||
) *RelayInternalAPI {
|
||||
return &RelayInternalAPI{
|
||||
db: db,
|
||||
fedClient: fedClient,
|
||||
rsAPI: rsAPI,
|
||||
keyRing: keyRing,
|
||||
producer: producer,
|
||||
presenceEnabledInbound: presenceEnabledInbound,
|
||||
serverName: serverName,
|
||||
relayingEnabled: relayingEnabled,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/storage/shared/receipt"
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/relayapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// SetRelayingEnabled implements api.RelayInternalAPI
|
||||
func (r *RelayInternalAPI) SetRelayingEnabled(enabled bool) {
|
||||
r.relayingEnabledMutex.Lock()
|
||||
defer r.relayingEnabledMutex.Unlock()
|
||||
r.relayingEnabled = enabled
|
||||
}
|
||||
|
||||
// RelayingEnabled implements api.RelayInternalAPI
|
||||
func (r *RelayInternalAPI) RelayingEnabled() bool {
|
||||
r.relayingEnabledMutex.Lock()
|
||||
defer r.relayingEnabledMutex.Unlock()
|
||||
return r.relayingEnabled
|
||||
}
|
||||
|
||||
// PerformRelayServerSync implements api.RelayInternalAPI
|
||||
func (r *RelayInternalAPI) PerformRelayServerSync(
|
||||
ctx context.Context,
|
||||
userID gomatrixserverlib.UserID,
|
||||
relayServer gomatrixserverlib.ServerName,
|
||||
) error {
|
||||
// Providing a default RelayEntry (EntryID = 0) is done to ask the relay if there are any
|
||||
// transactions available for this node.
|
||||
prevEntry := gomatrixserverlib.RelayEntry{}
|
||||
asyncResponse, err := r.fedClient.P2PGetTransactionFromRelay(ctx, userID, prevEntry, relayServer)
|
||||
if err != nil {
|
||||
logrus.Errorf("P2PGetTransactionFromRelay: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
r.processTransaction(&asyncResponse.Transaction)
|
||||
|
||||
prevEntry = gomatrixserverlib.RelayEntry{EntryID: asyncResponse.EntryID}
|
||||
for asyncResponse.EntriesQueued {
|
||||
// There are still more entries available for this node from the relay.
|
||||
logrus.Infof("Retrieving next entry from relay, previous: %v", prevEntry)
|
||||
asyncResponse, err = r.fedClient.P2PGetTransactionFromRelay(ctx, userID, prevEntry, relayServer)
|
||||
prevEntry = gomatrixserverlib.RelayEntry{EntryID: asyncResponse.EntryID}
|
||||
if err != nil {
|
||||
logrus.Errorf("P2PGetTransactionFromRelay: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
r.processTransaction(&asyncResponse.Transaction)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PerformStoreTransaction implements api.RelayInternalAPI
|
||||
func (r *RelayInternalAPI) PerformStoreTransaction(
|
||||
ctx context.Context,
|
||||
transaction gomatrixserverlib.Transaction,
|
||||
userID gomatrixserverlib.UserID,
|
||||
) error {
|
||||
logrus.Warnf("Storing transaction for %v", userID)
|
||||
receipt, err := r.db.StoreTransaction(ctx, transaction)
|
||||
if err != nil {
|
||||
logrus.Errorf("db.StoreTransaction: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
err = r.db.AssociateTransactionWithDestinations(
|
||||
ctx,
|
||||
map[gomatrixserverlib.UserID]struct{}{
|
||||
userID: {},
|
||||
},
|
||||
transaction.TransactionID,
|
||||
receipt)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// QueryTransactions implements api.RelayInternalAPI
|
||||
func (r *RelayInternalAPI) QueryTransactions(
|
||||
ctx context.Context,
|
||||
userID gomatrixserverlib.UserID,
|
||||
previousEntry gomatrixserverlib.RelayEntry,
|
||||
) (api.QueryRelayTransactionsResponse, error) {
|
||||
logrus.Infof("QueryTransactions for %s", userID.Raw())
|
||||
if previousEntry.EntryID > 0 {
|
||||
logrus.Infof("Cleaning previous entry (%v) from db for %s",
|
||||
previousEntry.EntryID,
|
||||
userID.Raw(),
|
||||
)
|
||||
prevReceipt := receipt.NewReceipt(previousEntry.EntryID)
|
||||
err := r.db.CleanTransactions(ctx, userID, []*receipt.Receipt{&prevReceipt})
|
||||
if err != nil {
|
||||
logrus.Errorf("db.CleanTransactions: %s", err.Error())
|
||||
return api.QueryRelayTransactionsResponse{}, err
|
||||
}
|
||||
}
|
||||
|
||||
transaction, receipt, err := r.db.GetTransaction(ctx, userID)
|
||||
if err != nil {
|
||||
logrus.Errorf("db.GetTransaction: %s", err.Error())
|
||||
return api.QueryRelayTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
response := api.QueryRelayTransactionsResponse{}
|
||||
if transaction != nil && receipt != nil {
|
||||
logrus.Infof("Obtained transaction (%v) for %s", transaction.TransactionID, userID.Raw())
|
||||
response.Transaction = *transaction
|
||||
response.EntryID = receipt.GetNID()
|
||||
response.EntriesQueued = true
|
||||
} else {
|
||||
logrus.Infof("No more entries in the queue for %s", userID.Raw())
|
||||
response.EntryID = 0
|
||||
response.EntriesQueued = false
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (r *RelayInternalAPI) processTransaction(txn *gomatrixserverlib.Transaction) {
|
||||
logrus.Warn("Processing transaction from relay server")
|
||||
mu := internal.NewMutexByRoom()
|
||||
t := internal.NewTxnReq(
|
||||
r.rsAPI,
|
||||
nil,
|
||||
r.serverName,
|
||||
r.keyRing,
|
||||
mu,
|
||||
r.producer,
|
||||
r.presenceEnabledInbound,
|
||||
txn.PDUs,
|
||||
txn.EDUs,
|
||||
txn.Origin,
|
||||
txn.TransactionID,
|
||||
txn.Destination)
|
||||
|
||||
t.ProcessTransaction(context.TODO())
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
fedAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/shared"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testFedClient struct {
|
||||
fedAPI.FederationClient
|
||||
shouldFail bool
|
||||
queryCount uint
|
||||
queueDepth uint
|
||||
}
|
||||
|
||||
func (f *testFedClient) P2PGetTransactionFromRelay(
|
||||
ctx context.Context,
|
||||
u gomatrixserverlib.UserID,
|
||||
prev gomatrixserverlib.RelayEntry,
|
||||
relayServer gomatrixserverlib.ServerName,
|
||||
) (res gomatrixserverlib.RespGetRelayTransaction, err error) {
|
||||
f.queryCount++
|
||||
if f.shouldFail {
|
||||
return res, fmt.Errorf("Error")
|
||||
}
|
||||
|
||||
res = gomatrixserverlib.RespGetRelayTransaction{
|
||||
Transaction: gomatrixserverlib.Transaction{},
|
||||
EntryID: 0,
|
||||
}
|
||||
if f.queueDepth > 0 {
|
||||
res.EntriesQueued = true
|
||||
} else {
|
||||
res.EntriesQueued = false
|
||||
}
|
||||
f.queueDepth -= 1
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func TestPerformRelayServerSync(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.Nil(t, err, "Invalid userID")
|
||||
|
||||
fedClient := &testFedClient{}
|
||||
relayAPI := NewRelayInternalAPI(
|
||||
&db, fedClient, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
err = relayAPI.PerformRelayServerSync(context.Background(), *userID, gomatrixserverlib.ServerName("relay"))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPerformRelayServerSyncFedError(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.Nil(t, err, "Invalid userID")
|
||||
|
||||
fedClient := &testFedClient{shouldFail: true}
|
||||
relayAPI := NewRelayInternalAPI(
|
||||
&db, fedClient, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
err = relayAPI.PerformRelayServerSync(context.Background(), *userID, gomatrixserverlib.ServerName("relay"))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestPerformRelayServerSyncRunsUntilQueueEmpty(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.Nil(t, err, "Invalid userID")
|
||||
|
||||
fedClient := &testFedClient{queueDepth: 2}
|
||||
relayAPI := NewRelayInternalAPI(
|
||||
&db, fedClient, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
err = relayAPI.PerformRelayServerSync(context.Background(), *userID, gomatrixserverlib.ServerName("relay"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, uint(3), fedClient.queryCount)
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package relayapi
|
||||
|
||||
import (
|
||||
"github.com/matrix-org/dendrite/federationapi/producers"
|
||||
"github.com/matrix-org/dendrite/relayapi/api"
|
||||
"github.com/matrix-org/dendrite/relayapi/internal"
|
||||
"github.com/matrix-org/dendrite/relayapi/routing"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage"
|
||||
rsAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// AddPublicRoutes sets up and registers HTTP handlers on the base API muxes for the FederationAPI component.
|
||||
func AddPublicRoutes(
|
||||
base *base.BaseDendrite,
|
||||
keyRing gomatrixserverlib.JSONVerifier,
|
||||
relayAPI api.RelayInternalAPI,
|
||||
) {
|
||||
fedCfg := &base.Cfg.FederationAPI
|
||||
|
||||
relay, ok := relayAPI.(*internal.RelayInternalAPI)
|
||||
if !ok {
|
||||
panic("relayapi.AddPublicRoutes called with a RelayInternalAPI impl which was not " +
|
||||
"RelayInternalAPI. This is a programming error.")
|
||||
}
|
||||
|
||||
routing.Setup(
|
||||
base.PublicFederationAPIMux,
|
||||
fedCfg,
|
||||
relay,
|
||||
keyRing,
|
||||
)
|
||||
}
|
||||
|
||||
func NewRelayInternalAPI(
|
||||
base *base.BaseDendrite,
|
||||
fedClient *gomatrixserverlib.FederationClient,
|
||||
rsAPI rsAPI.RoomserverInternalAPI,
|
||||
keyRing *gomatrixserverlib.KeyRing,
|
||||
producer *producers.SyncAPIProducer,
|
||||
relayingEnabled bool,
|
||||
) api.RelayInternalAPI {
|
||||
cfg := &base.Cfg.RelayAPI
|
||||
|
||||
relayDB, err := storage.NewDatabase(base, &cfg.Database, base.Caches, base.Cfg.Global.IsLocalServerName)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Panic("failed to connect to relay db")
|
||||
}
|
||||
|
||||
return internal.NewRelayInternalAPI(
|
||||
relayDB,
|
||||
fedClient,
|
||||
rsAPI,
|
||||
keyRing,
|
||||
producer,
|
||||
base.Cfg.Global.Presence.EnableInbound,
|
||||
base.Cfg.Global.ServerName,
|
||||
relayingEnabled,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package relayapi_test
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
|
||||
"github.com/matrix-org/dendrite/relayapi"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/dendrite/test/testrig"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCreateNewRelayInternalAPI(t *testing.T) {
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
base, close := testrig.CreateBaseDendrite(t, dbType)
|
||||
defer close()
|
||||
|
||||
relayAPI := relayapi.NewRelayInternalAPI(base, nil, nil, nil, nil, true)
|
||||
assert.NotNil(t, relayAPI)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateRelayInternalInvalidDatabasePanics(t *testing.T) {
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
base, close := testrig.CreateBaseDendrite(t, dbType)
|
||||
if dbType == test.DBTypeSQLite {
|
||||
base.Cfg.RelayAPI.Database.ConnectionString = "file:"
|
||||
} else {
|
||||
base.Cfg.RelayAPI.Database.ConnectionString = "test"
|
||||
}
|
||||
defer close()
|
||||
|
||||
assert.Panics(t, func() {
|
||||
relayapi.NewRelayInternalAPI(base, nil, nil, nil, nil, true)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateInvalidRelayPublicRoutesPanics(t *testing.T) {
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
base, close := testrig.CreateBaseDendrite(t, dbType)
|
||||
defer close()
|
||||
|
||||
assert.Panics(t, func() {
|
||||
relayapi.AddPublicRoutes(base, nil, nil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func createGetRelayTxnHTTPRequest(serverName gomatrixserverlib.ServerName, userID string) *http.Request {
|
||||
_, sk, _ := ed25519.GenerateKey(nil)
|
||||
keyID := signing.KeyID
|
||||
pk := sk.Public().(ed25519.PublicKey)
|
||||
origin := gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
||||
req := gomatrixserverlib.NewFederationRequest("GET", origin, serverName, "/_matrix/federation/v1/relay_txn/"+userID)
|
||||
content := gomatrixserverlib.RelayEntry{EntryID: 0}
|
||||
req.SetContent(content)
|
||||
req.Sign(origin, gomatrixserverlib.KeyID(keyID), sk)
|
||||
httpreq, _ := req.HTTPRequest()
|
||||
vars := map[string]string{"userID": userID}
|
||||
httpreq = mux.SetURLVars(httpreq, vars)
|
||||
return httpreq
|
||||
}
|
||||
|
||||
type sendRelayContent struct {
|
||||
PDUs []json.RawMessage `json:"pdus"`
|
||||
EDUs []gomatrixserverlib.EDU `json:"edus"`
|
||||
}
|
||||
|
||||
func createSendRelayTxnHTTPRequest(serverName gomatrixserverlib.ServerName, txnID string, userID string) *http.Request {
|
||||
_, sk, _ := ed25519.GenerateKey(nil)
|
||||
keyID := signing.KeyID
|
||||
pk := sk.Public().(ed25519.PublicKey)
|
||||
origin := gomatrixserverlib.ServerName(hex.EncodeToString(pk))
|
||||
req := gomatrixserverlib.NewFederationRequest("PUT", origin, serverName, "/_matrix/federation/v1/send_relay/"+txnID+"/"+userID)
|
||||
content := sendRelayContent{}
|
||||
req.SetContent(content)
|
||||
req.Sign(origin, gomatrixserverlib.KeyID(keyID), sk)
|
||||
httpreq, _ := req.HTTPRequest()
|
||||
vars := map[string]string{"userID": userID, "txnID": txnID}
|
||||
httpreq = mux.SetURLVars(httpreq, vars)
|
||||
return httpreq
|
||||
}
|
||||
|
||||
func TestCreateRelayPublicRoutes(t *testing.T) {
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
base, close := testrig.CreateBaseDendrite(t, dbType)
|
||||
defer close()
|
||||
|
||||
relayAPI := relayapi.NewRelayInternalAPI(base, nil, nil, nil, nil, true)
|
||||
assert.NotNil(t, relayAPI)
|
||||
|
||||
serverKeyAPI := &signing.YggdrasilKeys{}
|
||||
keyRing := serverKeyAPI.KeyRing()
|
||||
relayapi.AddPublicRoutes(base, keyRing, relayAPI)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
req *http.Request
|
||||
wantCode int
|
||||
}{
|
||||
{
|
||||
name: "relay_txn invalid user id",
|
||||
req: createGetRelayTxnHTTPRequest(base.Cfg.Global.ServerName, "user:local"),
|
||||
wantCode: 400,
|
||||
},
|
||||
{
|
||||
name: "relay_txn valid user id",
|
||||
req: createGetRelayTxnHTTPRequest(base.Cfg.Global.ServerName, "@user:local"),
|
||||
wantCode: 200,
|
||||
},
|
||||
{
|
||||
name: "send_relay invalid user id",
|
||||
req: createSendRelayTxnHTTPRequest(base.Cfg.Global.ServerName, "123", "user:local"),
|
||||
wantCode: 400,
|
||||
},
|
||||
{
|
||||
name: "send_relay valid user id",
|
||||
req: createSendRelayTxnHTTPRequest(base.Cfg.Global.ServerName, "123", "@user:local"),
|
||||
wantCode: 200,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
w := httptest.NewRecorder()
|
||||
base.PublicFederationAPIMux.ServeHTTP(w, tc.req)
|
||||
if w.Code != tc.wantCode {
|
||||
t.Fatalf("%s: got HTTP %d want %d", tc.name, w.Code, tc.wantCode)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDisableRelayPublicRoutes(t *testing.T) {
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
base, close := testrig.CreateBaseDendrite(t, dbType)
|
||||
defer close()
|
||||
|
||||
relayAPI := relayapi.NewRelayInternalAPI(base, nil, nil, nil, nil, false)
|
||||
assert.NotNil(t, relayAPI)
|
||||
|
||||
serverKeyAPI := &signing.YggdrasilKeys{}
|
||||
keyRing := serverKeyAPI.KeyRing()
|
||||
relayapi.AddPublicRoutes(base, keyRing, relayAPI)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
req *http.Request
|
||||
wantCode int
|
||||
}{
|
||||
{
|
||||
name: "relay_txn valid user id",
|
||||
req: createGetRelayTxnHTTPRequest(base.Cfg.Global.ServerName, "@user:local"),
|
||||
wantCode: 404,
|
||||
},
|
||||
{
|
||||
name: "send_relay valid user id",
|
||||
req: createSendRelayTxnHTTPRequest(base.Cfg.Global.ServerName, "123", "@user:local"),
|
||||
wantCode: 404,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
w := httptest.NewRecorder()
|
||||
base.PublicFederationAPIMux.ServeHTTP(w, tc.req)
|
||||
if w.Code != tc.wantCode {
|
||||
t.Fatalf("%s: got HTTP %d want %d", tc.name, w.Code, tc.wantCode)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/relayapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// GetTransactionFromRelay implements GET /_matrix/federation/v1/relay_txn/{userID}
|
||||
// This endpoint can be extracted into a separate relay server service.
|
||||
func GetTransactionFromRelay(
|
||||
httpReq *http.Request,
|
||||
fedReq *gomatrixserverlib.FederationRequest,
|
||||
relayAPI api.RelayInternalAPI,
|
||||
userID gomatrixserverlib.UserID,
|
||||
) util.JSONResponse {
|
||||
logrus.Infof("Processing relay_txn for %s", userID.Raw())
|
||||
|
||||
var previousEntry gomatrixserverlib.RelayEntry
|
||||
if err := json.Unmarshal(fedReq.Content(), &previousEntry); err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: jsonerror.BadJSON("invalid json provided"),
|
||||
}
|
||||
}
|
||||
if previousEntry.EntryID < 0 {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: jsonerror.BadJSON("Invalid entry id provided. Must be >= 0."),
|
||||
}
|
||||
}
|
||||
logrus.Infof("Previous entry provided: %v", previousEntry.EntryID)
|
||||
|
||||
response, err := relayAPI.QueryTransactions(httpReq.Context(), userID, previousEntry)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: gomatrixserverlib.RespGetRelayTransaction{
|
||||
Transaction: response.Transaction,
|
||||
EntryID: response.EntryID,
|
||||
EntriesQueued: response.EntriesQueued,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package routing_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/relayapi/internal"
|
||||
"github.com/matrix-org/dendrite/relayapi/routing"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/shared"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func createQuery(
|
||||
userID gomatrixserverlib.UserID,
|
||||
prevEntry gomatrixserverlib.RelayEntry,
|
||||
) gomatrixserverlib.FederationRequest {
|
||||
var federationPathPrefixV1 = "/_matrix/federation/v1"
|
||||
path := federationPathPrefixV1 + "/relay_txn/" + userID.Raw()
|
||||
request := gomatrixserverlib.NewFederationRequest("GET", userID.Domain(), "relay", path)
|
||||
request.SetContent(prevEntry)
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
func TestGetEmptyDatabaseReturnsNothing(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
transaction := createTransaction()
|
||||
|
||||
_, err = db.StoreTransaction(context.Background(), transaction)
|
||||
assert.NoError(t, err, "Failed to store transaction")
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
request := createQuery(*userID, gomatrixserverlib.RelayEntry{})
|
||||
response := routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID)
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
|
||||
jsonResponse := response.JSON.(gomatrixserverlib.RespGetRelayTransaction)
|
||||
assert.Equal(t, false, jsonResponse.EntriesQueued)
|
||||
assert.Equal(t, gomatrixserverlib.Transaction{}, jsonResponse.Transaction)
|
||||
|
||||
count, err := db.GetTransactionCount(context.Background(), *userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Zero(t, count)
|
||||
}
|
||||
|
||||
func TestGetInvalidPrevEntryFails(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
transaction := createTransaction()
|
||||
|
||||
_, err = db.StoreTransaction(context.Background(), transaction)
|
||||
assert.NoError(t, err, "Failed to store transaction")
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
request := createQuery(*userID, gomatrixserverlib.RelayEntry{EntryID: -1})
|
||||
response := routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID)
|
||||
assert.Equal(t, http.StatusInternalServerError, response.Code)
|
||||
}
|
||||
|
||||
func TestGetReturnsSavedTransaction(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
transaction := createTransaction()
|
||||
receipt, err := db.StoreTransaction(context.Background(), transaction)
|
||||
assert.NoError(t, err, "Failed to store transaction")
|
||||
|
||||
err = db.AssociateTransactionWithDestinations(
|
||||
context.Background(),
|
||||
map[gomatrixserverlib.UserID]struct{}{
|
||||
*userID: {},
|
||||
},
|
||||
transaction.TransactionID,
|
||||
receipt)
|
||||
assert.NoError(t, err, "Failed to associate transaction with user")
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
request := createQuery(*userID, gomatrixserverlib.RelayEntry{})
|
||||
response := routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID)
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
|
||||
jsonResponse := response.JSON.(gomatrixserverlib.RespGetRelayTransaction)
|
||||
assert.True(t, jsonResponse.EntriesQueued)
|
||||
assert.Equal(t, transaction, jsonResponse.Transaction)
|
||||
|
||||
// And once more to clear the queue
|
||||
request = createQuery(*userID, gomatrixserverlib.RelayEntry{EntryID: jsonResponse.EntryID})
|
||||
response = routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID)
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
|
||||
jsonResponse = response.JSON.(gomatrixserverlib.RespGetRelayTransaction)
|
||||
assert.False(t, jsonResponse.EntriesQueued)
|
||||
assert.Equal(t, gomatrixserverlib.Transaction{}, jsonResponse.Transaction)
|
||||
|
||||
count, err := db.GetTransactionCount(context.Background(), *userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Zero(t, count)
|
||||
}
|
||||
|
||||
func TestGetReturnsMultipleSavedTransactions(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
transaction := createTransaction()
|
||||
receipt, err := db.StoreTransaction(context.Background(), transaction)
|
||||
assert.NoError(t, err, "Failed to store transaction")
|
||||
|
||||
err = db.AssociateTransactionWithDestinations(
|
||||
context.Background(),
|
||||
map[gomatrixserverlib.UserID]struct{}{
|
||||
*userID: {},
|
||||
},
|
||||
transaction.TransactionID,
|
||||
receipt)
|
||||
assert.NoError(t, err, "Failed to associate transaction with user")
|
||||
|
||||
transaction2 := createTransaction()
|
||||
receipt2, err := db.StoreTransaction(context.Background(), transaction2)
|
||||
assert.NoError(t, err, "Failed to store transaction")
|
||||
|
||||
err = db.AssociateTransactionWithDestinations(
|
||||
context.Background(),
|
||||
map[gomatrixserverlib.UserID]struct{}{
|
||||
*userID: {},
|
||||
},
|
||||
transaction2.TransactionID,
|
||||
receipt2)
|
||||
assert.NoError(t, err, "Failed to associate transaction with user")
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
request := createQuery(*userID, gomatrixserverlib.RelayEntry{})
|
||||
response := routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID)
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
|
||||
jsonResponse := response.JSON.(gomatrixserverlib.RespGetRelayTransaction)
|
||||
assert.True(t, jsonResponse.EntriesQueued)
|
||||
assert.Equal(t, transaction, jsonResponse.Transaction)
|
||||
|
||||
request = createQuery(*userID, gomatrixserverlib.RelayEntry{EntryID: jsonResponse.EntryID})
|
||||
response = routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID)
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
|
||||
jsonResponse = response.JSON.(gomatrixserverlib.RespGetRelayTransaction)
|
||||
assert.True(t, jsonResponse.EntriesQueued)
|
||||
assert.Equal(t, transaction2, jsonResponse.Transaction)
|
||||
|
||||
// And once more to clear the queue
|
||||
request = createQuery(*userID, gomatrixserverlib.RelayEntry{EntryID: jsonResponse.EntryID})
|
||||
response = routing.GetTransactionFromRelay(httpReq, &request, relayAPI, *userID)
|
||||
assert.Equal(t, http.StatusOK, response.Code)
|
||||
|
||||
jsonResponse = response.JSON.(gomatrixserverlib.RespGetRelayTransaction)
|
||||
assert.False(t, jsonResponse.EntriesQueued)
|
||||
assert.Equal(t, gomatrixserverlib.Transaction{}, jsonResponse.Transaction)
|
||||
|
||||
count, err := db.GetTransactionCount(context.Background(), *userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Zero(t, count)
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
relayInternal "github.com/matrix-org/dendrite/relayapi/internal"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Setup registers HTTP handlers with the given ServeMux.
|
||||
// The provided publicAPIMux MUST have `UseEncodedPath()` enabled or else routes will incorrectly
|
||||
// path unescape twice (once from the router, once from MakeRelayAPI). We need to have this enabled
|
||||
// so we can decode paths like foo/bar%2Fbaz as [foo, bar/baz] - by default it will decode to [foo, bar, baz]
|
||||
func Setup(
|
||||
fedMux *mux.Router,
|
||||
cfg *config.FederationAPI,
|
||||
relayAPI *relayInternal.RelayInternalAPI,
|
||||
keys gomatrixserverlib.JSONVerifier,
|
||||
) {
|
||||
v1fedmux := fedMux.PathPrefix("/v1").Subrouter()
|
||||
|
||||
v1fedmux.Handle("/send_relay/{txnID}/{userID}", MakeRelayAPI(
|
||||
"send_relay_transaction", "", cfg.Matrix.IsLocalServerName, keys,
|
||||
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
|
||||
logrus.Infof("Handling send_relay from: %s", request.Origin())
|
||||
if !relayAPI.RelayingEnabled() {
|
||||
logrus.Warnf("Dropping send_relay from: %s", request.Origin())
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
}
|
||||
}
|
||||
|
||||
userID, err := gomatrixserverlib.NewUserID(vars["userID"], false)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.InvalidUsername("Username was invalid"),
|
||||
}
|
||||
}
|
||||
return SendTransactionToRelay(
|
||||
httpReq, request, relayAPI, gomatrixserverlib.TransactionID(vars["txnID"]),
|
||||
*userID,
|
||||
)
|
||||
},
|
||||
)).Methods(http.MethodPut, http.MethodOptions)
|
||||
|
||||
v1fedmux.Handle("/relay_txn/{userID}", MakeRelayAPI(
|
||||
"get_relay_transaction", "", cfg.Matrix.IsLocalServerName, keys,
|
||||
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
|
||||
logrus.Infof("Handling relay_txn from: %s", request.Origin())
|
||||
if !relayAPI.RelayingEnabled() {
|
||||
logrus.Warnf("Dropping relay_txn from: %s", request.Origin())
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
}
|
||||
}
|
||||
|
||||
userID, err := gomatrixserverlib.NewUserID(vars["userID"], false)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.InvalidUsername("Username was invalid"),
|
||||
}
|
||||
}
|
||||
return GetTransactionFromRelay(httpReq, request, relayAPI, *userID)
|
||||
},
|
||||
)).Methods(http.MethodGet, http.MethodOptions)
|
||||
}
|
||||
|
||||
// MakeRelayAPI makes an http.Handler that checks matrix relay authentication.
|
||||
func MakeRelayAPI(
|
||||
metricsName string, serverName gomatrixserverlib.ServerName,
|
||||
isLocalServerName func(gomatrixserverlib.ServerName) bool,
|
||||
keyRing gomatrixserverlib.JSONVerifier,
|
||||
f func(*http.Request, *gomatrixserverlib.FederationRequest, map[string]string) util.JSONResponse,
|
||||
) http.Handler {
|
||||
h := func(req *http.Request) util.JSONResponse {
|
||||
fedReq, errResp := gomatrixserverlib.VerifyHTTPRequest(
|
||||
req, time.Now(), serverName, isLocalServerName, keyRing,
|
||||
)
|
||||
if fedReq == nil {
|
||||
return errResp
|
||||
}
|
||||
// add the user to Sentry, if enabled
|
||||
hub := sentry.GetHubFromContext(req.Context())
|
||||
if hub != nil {
|
||||
hub.Scope().SetTag("origin", string(fedReq.Origin()))
|
||||
hub.Scope().SetTag("uri", fedReq.RequestURI())
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if hub != nil {
|
||||
hub.CaptureException(fmt.Errorf("%s panicked", req.URL.Path))
|
||||
}
|
||||
// re-panic to return the 500
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.MatrixErrorResponse(400, "M_UNRECOGNISED", "badly encoded query params")
|
||||
}
|
||||
|
||||
jsonRes := f(req, fedReq, vars)
|
||||
// do not log 4xx as errors as they are client fails, not server fails
|
||||
if hub != nil && jsonRes.Code >= 500 {
|
||||
hub.Scope().SetExtra("response", jsonRes)
|
||||
hub.CaptureException(fmt.Errorf("%s returned HTTP %d", req.URL.Path, jsonRes.Code))
|
||||
}
|
||||
return jsonRes
|
||||
}
|
||||
return httputil.MakeExternalAPI(metricsName, h)
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/relayapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// SendTransactionToRelay implements PUT /_matrix/federation/v1/send_relay/{txnID}/{userID}
|
||||
// This endpoint can be extracted into a separate relay server service.
|
||||
func SendTransactionToRelay(
|
||||
httpReq *http.Request,
|
||||
fedReq *gomatrixserverlib.FederationRequest,
|
||||
relayAPI api.RelayInternalAPI,
|
||||
txnID gomatrixserverlib.TransactionID,
|
||||
userID gomatrixserverlib.UserID,
|
||||
) util.JSONResponse {
|
||||
logrus.Infof("Processing send_relay for %s", userID.Raw())
|
||||
|
||||
var txnEvents gomatrixserverlib.RelayEvents
|
||||
if err := json.Unmarshal(fedReq.Content(), &txnEvents); err != nil {
|
||||
logrus.Info("The request body could not be decoded into valid JSON." + err.Error())
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.NotJSON("The request body could not be decoded into valid JSON." + err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
// Transactions are limited in size; they can have at most 50 PDUs and 100 EDUs.
|
||||
// https://matrix.org/docs/spec/server_server/latest#transactions
|
||||
if len(txnEvents.PDUs) > 50 || len(txnEvents.EDUs) > 100 {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.BadJSON("max 50 pdus / 100 edus"),
|
||||
}
|
||||
}
|
||||
|
||||
t := gomatrixserverlib.Transaction{}
|
||||
t.PDUs = txnEvents.PDUs
|
||||
t.EDUs = txnEvents.EDUs
|
||||
t.Origin = fedReq.Origin()
|
||||
t.TransactionID = txnID
|
||||
t.Destination = userID.Domain()
|
||||
|
||||
util.GetLogger(httpReq.Context()).Warnf("Received transaction %q from %q containing %d PDUs, %d EDUs", txnID, fedReq.Origin(), len(t.PDUs), len(t.EDUs))
|
||||
|
||||
err := relayAPI.PerformStoreTransaction(httpReq.Context(), t, userID)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: jsonerror.BadJSON("could not store the transaction for forwarding"),
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{Code: 200}
|
||||
}
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package routing_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/relayapi/internal"
|
||||
"github.com/matrix-org/dendrite/relayapi/routing"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/shared"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
testOrigin = gomatrixserverlib.ServerName("kaer.morhen")
|
||||
)
|
||||
|
||||
func createTransaction() gomatrixserverlib.Transaction {
|
||||
txn := gomatrixserverlib.Transaction{}
|
||||
txn.PDUs = []json.RawMessage{
|
||||
[]byte(`{"auth_events":[["$0ok8ynDp7kjc95e3:kaer.morhen",{"sha256":"sWCi6Ckp9rDimQON+MrUlNRkyfZ2tjbPbWfg2NMB18Q"}],["$LEwEu0kxrtu5fOiS:kaer.morhen",{"sha256":"1aKajq6DWHru1R1HJjvdWMEavkJJHGaTmPvfuERUXaA"}]],"content":{"body":"Test Message"},"depth":5,"event_id":"$gl2T9l3qm0kUbiIJ:kaer.morhen","hashes":{"sha256":"Qx3nRMHLDPSL5hBAzuX84FiSSP0K0Kju2iFoBWH4Za8"},"origin":"kaer.morhen","origin_server_ts":0,"prev_events":[["$UKNe10XzYzG0TeA9:kaer.morhen",{"sha256":"KtSRyMjt0ZSjsv2koixTRCxIRCGoOp6QrKscsW97XRo"}]],"room_id":"!roomid:kaer.morhen","sender":"@userid:kaer.morhen","signatures":{"kaer.morhen":{"ed25519:auto":"sqDgv3EG7ml5VREzmT9aZeBpS4gAPNIaIeJOwqjDhY0GPU/BcpX5wY4R7hYLrNe5cChgV+eFy/GWm1Zfg5FfDg"}},"type":"m.room.message"}`),
|
||||
}
|
||||
txn.Origin = testOrigin
|
||||
return txn
|
||||
}
|
||||
|
||||
func createFederationRequest(
|
||||
userID gomatrixserverlib.UserID,
|
||||
txnID gomatrixserverlib.TransactionID,
|
||||
origin gomatrixserverlib.ServerName,
|
||||
destination gomatrixserverlib.ServerName,
|
||||
content interface{},
|
||||
) gomatrixserverlib.FederationRequest {
|
||||
var federationPathPrefixV1 = "/_matrix/federation/v1"
|
||||
path := federationPathPrefixV1 + "/send_relay/" + string(txnID) + "/" + userID.Raw()
|
||||
request := gomatrixserverlib.NewFederationRequest("PUT", origin, destination, path)
|
||||
request.SetContent(content)
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
func TestForwardEmptyReturnsOk(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
txn := createTransaction()
|
||||
request := createFederationRequest(*userID, txn.TransactionID, txn.Origin, txn.Destination, txn)
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
response := routing.SendTransactionToRelay(httpReq, &request, relayAPI, "1", *userID)
|
||||
|
||||
assert.Equal(t, 200, response.Code)
|
||||
}
|
||||
|
||||
func TestForwardBadJSONReturnsError(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
type BadData struct {
|
||||
Field bool `json:"pdus"`
|
||||
}
|
||||
content := BadData{
|
||||
Field: false,
|
||||
}
|
||||
txn := createTransaction()
|
||||
request := createFederationRequest(*userID, txn.TransactionID, txn.Origin, txn.Destination, content)
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
response := routing.SendTransactionToRelay(httpReq, &request, relayAPI, "1", *userID)
|
||||
|
||||
assert.NotEqual(t, 200, response.Code)
|
||||
}
|
||||
|
||||
func TestForwardTooManyPDUsReturnsError(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
type BadData struct {
|
||||
Field []json.RawMessage `json:"pdus"`
|
||||
}
|
||||
content := BadData{
|
||||
Field: []json.RawMessage{},
|
||||
}
|
||||
for i := 0; i < 51; i++ {
|
||||
content.Field = append(content.Field, []byte{})
|
||||
}
|
||||
assert.Greater(t, len(content.Field), 50)
|
||||
|
||||
txn := createTransaction()
|
||||
request := createFederationRequest(*userID, txn.TransactionID, txn.Origin, txn.Destination, content)
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
response := routing.SendTransactionToRelay(httpReq, &request, relayAPI, "1", *userID)
|
||||
|
||||
assert.NotEqual(t, 200, response.Code)
|
||||
}
|
||||
|
||||
func TestForwardTooManyEDUsReturnsError(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
type BadData struct {
|
||||
Field []gomatrixserverlib.EDU `json:"edus"`
|
||||
}
|
||||
content := BadData{
|
||||
Field: []gomatrixserverlib.EDU{},
|
||||
}
|
||||
for i := 0; i < 101; i++ {
|
||||
content.Field = append(content.Field, gomatrixserverlib.EDU{Type: gomatrixserverlib.MTyping})
|
||||
}
|
||||
assert.Greater(t, len(content.Field), 100)
|
||||
|
||||
txn := createTransaction()
|
||||
request := createFederationRequest(*userID, txn.TransactionID, txn.Origin, txn.Destination, content)
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
response := routing.SendTransactionToRelay(httpReq, &request, relayAPI, "1", *userID)
|
||||
|
||||
assert.NotEqual(t, 200, response.Code)
|
||||
}
|
||||
|
||||
func TestUniqueTransactionStoredInDatabase(t *testing.T) {
|
||||
testDB := test.NewInMemoryRelayDatabase()
|
||||
db := shared.Database{
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
RelayQueue: testDB,
|
||||
RelayQueueJSON: testDB,
|
||||
}
|
||||
httpReq := &http.Request{}
|
||||
userID, err := gomatrixserverlib.NewUserID("@local:domain", false)
|
||||
assert.NoError(t, err, "Invalid userID")
|
||||
|
||||
txn := createTransaction()
|
||||
request := createFederationRequest(*userID, txn.TransactionID, txn.Origin, txn.Destination, txn)
|
||||
|
||||
relayAPI := internal.NewRelayInternalAPI(
|
||||
&db, nil, nil, nil, nil, false, "", true,
|
||||
)
|
||||
|
||||
response := routing.SendTransactionToRelay(
|
||||
httpReq, &request, relayAPI, txn.TransactionID, *userID)
|
||||
transaction, _, err := db.GetTransaction(context.Background(), *userID)
|
||||
assert.NoError(t, err, "Failed retrieving transaction")
|
||||
|
||||
transactionCount, err := db.GetTransactionCount(context.Background(), *userID)
|
||||
assert.NoError(t, err, "Failed retrieving transaction count")
|
||||
|
||||
assert.Equal(t, 200, response.Code)
|
||||
assert.Equal(t, int64(1), transactionCount)
|
||||
assert.Equal(t, txn.TransactionID, transaction.TransactionID)
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/storage/shared/receipt"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type Database interface {
|
||||
// Adds a new transaction to the queue json table.
|
||||
// Adding a duplicate transaction will result in a new row being added and a new unique nid.
|
||||
// return: unique nid representing this entry.
|
||||
StoreTransaction(ctx context.Context, txn gomatrixserverlib.Transaction) (*receipt.Receipt, error)
|
||||
|
||||
// Adds a new transaction_id: server_name mapping with associated json table nid to the queue
|
||||
// entry table for each provided destination.
|
||||
AssociateTransactionWithDestinations(ctx context.Context, destinations map[gomatrixserverlib.UserID]struct{}, transactionID gomatrixserverlib.TransactionID, dbReceipt *receipt.Receipt) error
|
||||
|
||||
// Removes every server_name: receipt pair provided from the queue entries table.
|
||||
// Will then remove every entry for each receipt provided from the queue json table.
|
||||
// If any of the entries don't exist in either table, nothing will happen for that entry and
|
||||
// an error will not be generated.
|
||||
CleanTransactions(ctx context.Context, userID gomatrixserverlib.UserID, receipts []*receipt.Receipt) error
|
||||
|
||||
// Gets the oldest transaction for the provided server_name.
|
||||
// If no transactions exist, returns nil and no error.
|
||||
GetTransaction(ctx context.Context, userID gomatrixserverlib.UserID) (*gomatrixserverlib.Transaction, *receipt.Receipt, error)
|
||||
|
||||
// Gets the number of transactions being stored for the provided server_name.
|
||||
// If the server doesn't exist in the database then 0 is returned with no error.
|
||||
GetTransactionCount(ctx context.Context, userID gomatrixserverlib.UserID) (int64, error)
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
)
|
||||
|
||||
const relayQueueJSONSchema = `
|
||||
-- The relayapi_queue_json table contains event contents that
|
||||
-- we are storing for future forwarding.
|
||||
CREATE TABLE IF NOT EXISTS relayapi_queue_json (
|
||||
-- The JSON NID. This allows cross-referencing to find the JSON blob.
|
||||
json_nid BIGSERIAL,
|
||||
-- The JSON body. Text so that we preserve UTF-8.
|
||||
json_body TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS relayapi_queue_json_json_nid_idx
|
||||
ON relayapi_queue_json (json_nid);
|
||||
`
|
||||
|
||||
const insertQueueJSONSQL = "" +
|
||||
"INSERT INTO relayapi_queue_json (json_body)" +
|
||||
" VALUES ($1)" +
|
||||
" RETURNING json_nid"
|
||||
|
||||
const deleteQueueJSONSQL = "" +
|
||||
"DELETE FROM relayapi_queue_json WHERE json_nid = ANY($1)"
|
||||
|
||||
const selectQueueJSONSQL = "" +
|
||||
"SELECT json_nid, json_body FROM relayapi_queue_json" +
|
||||
" WHERE json_nid = ANY($1)"
|
||||
|
||||
type relayQueueJSONStatements struct {
|
||||
db *sql.DB
|
||||
insertJSONStmt *sql.Stmt
|
||||
deleteJSONStmt *sql.Stmt
|
||||
selectJSONStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func NewPostgresRelayQueueJSONTable(db *sql.DB) (s *relayQueueJSONStatements, err error) {
|
||||
s = &relayQueueJSONStatements{
|
||||
db: db,
|
||||
}
|
||||
_, err = s.db.Exec(relayQueueJSONSchema)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return s, sqlutil.StatementList{
|
||||
{&s.insertJSONStmt, insertQueueJSONSQL},
|
||||
{&s.deleteJSONStmt, deleteQueueJSONSQL},
|
||||
{&s.selectJSONStmt, selectQueueJSONSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
func (s *relayQueueJSONStatements) InsertQueueJSON(
|
||||
ctx context.Context, txn *sql.Tx, json string,
|
||||
) (int64, error) {
|
||||
stmt := sqlutil.TxStmt(txn, s.insertJSONStmt)
|
||||
var lastid int64
|
||||
if err := stmt.QueryRowContext(ctx, json).Scan(&lastid); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return lastid, nil
|
||||
}
|
||||
|
||||
func (s *relayQueueJSONStatements) DeleteQueueJSON(
|
||||
ctx context.Context, txn *sql.Tx, nids []int64,
|
||||
) error {
|
||||
stmt := sqlutil.TxStmt(txn, s.deleteJSONStmt)
|
||||
_, err := stmt.ExecContext(ctx, pq.Int64Array(nids))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *relayQueueJSONStatements) SelectQueueJSON(
|
||||
ctx context.Context, txn *sql.Tx, jsonNIDs []int64,
|
||||
) (map[int64][]byte, error) {
|
||||
blobs := map[int64][]byte{}
|
||||
stmt := sqlutil.TxStmt(txn, s.selectJSONStmt)
|
||||
rows, err := stmt.QueryContext(ctx, pq.Int64Array(jsonNIDs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "selectJSON: rows.close() failed")
|
||||
for rows.Next() {
|
||||
var nid int64
|
||||
var blob []byte
|
||||
if err = rows.Scan(&nid, &blob); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blobs[nid] = blob
|
||||
}
|
||||
return blobs, err
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
const relayQueueSchema = `
|
||||
CREATE TABLE IF NOT EXISTS relayapi_queue (
|
||||
-- The transaction ID that was generated before persisting the event.
|
||||
transaction_id TEXT NOT NULL,
|
||||
-- The destination server that we will send the event to.
|
||||
server_name TEXT NOT NULL,
|
||||
-- The JSON NID from the relayapi_queue_json table.
|
||||
json_nid BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS relayapi_queue_queue_json_nid_idx
|
||||
ON relayapi_queue (json_nid, server_name);
|
||||
CREATE INDEX IF NOT EXISTS relayapi_queue_json_nid_idx
|
||||
ON relayapi_queue (json_nid);
|
||||
CREATE INDEX IF NOT EXISTS relayapi_queue_server_name_idx
|
||||
ON relayapi_queue (server_name);
|
||||
`
|
||||
|
||||
const insertQueueEntrySQL = "" +
|
||||
"INSERT INTO relayapi_queue (transaction_id, server_name, json_nid)" +
|
||||
" VALUES ($1, $2, $3)"
|
||||
|
||||
const deleteQueueEntriesSQL = "" +
|
||||
"DELETE FROM relayapi_queue WHERE server_name = $1 AND json_nid = ANY($2)"
|
||||
|
||||
const selectQueueEntriesSQL = "" +
|
||||
"SELECT json_nid FROM relayapi_queue" +
|
||||
" WHERE server_name = $1" +
|
||||
" ORDER BY json_nid" +
|
||||
" LIMIT $2"
|
||||
|
||||
const selectQueueEntryCountSQL = "" +
|
||||
"SELECT COUNT(*) FROM relayapi_queue" +
|
||||
" WHERE server_name = $1"
|
||||
|
||||
type relayQueueStatements struct {
|
||||
db *sql.DB
|
||||
insertQueueEntryStmt *sql.Stmt
|
||||
deleteQueueEntriesStmt *sql.Stmt
|
||||
selectQueueEntriesStmt *sql.Stmt
|
||||
selectQueueEntryCountStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func NewPostgresRelayQueueTable(
|
||||
db *sql.DB,
|
||||
) (s *relayQueueStatements, err error) {
|
||||
s = &relayQueueStatements{
|
||||
db: db,
|
||||
}
|
||||
_, err = s.db.Exec(relayQueueSchema)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return s, sqlutil.StatementList{
|
||||
{&s.insertQueueEntryStmt, insertQueueEntrySQL},
|
||||
{&s.deleteQueueEntriesStmt, deleteQueueEntriesSQL},
|
||||
{&s.selectQueueEntriesStmt, selectQueueEntriesSQL},
|
||||
{&s.selectQueueEntryCountStmt, selectQueueEntryCountSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
func (s *relayQueueStatements) InsertQueueEntry(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
transactionID gomatrixserverlib.TransactionID,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
nid int64,
|
||||
) error {
|
||||
stmt := sqlutil.TxStmt(txn, s.insertQueueEntryStmt)
|
||||
_, err := stmt.ExecContext(
|
||||
ctx,
|
||||
transactionID, // the transaction ID that we initially attempted
|
||||
serverName, // destination server name
|
||||
nid, // JSON blob NID
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *relayQueueStatements) DeleteQueueEntries(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
jsonNIDs []int64,
|
||||
) error {
|
||||
stmt := sqlutil.TxStmt(txn, s.deleteQueueEntriesStmt)
|
||||
_, err := stmt.ExecContext(ctx, serverName, pq.Int64Array(jsonNIDs))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *relayQueueStatements) SelectQueueEntries(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
limit int,
|
||||
) ([]int64, error) {
|
||||
stmt := sqlutil.TxStmt(txn, s.selectQueueEntriesStmt)
|
||||
rows, err := stmt.QueryContext(ctx, serverName, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "queueFromStmt: rows.close() failed")
|
||||
var result []int64
|
||||
for rows.Next() {
|
||||
var nid int64
|
||||
if err = rows.Scan(&nid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, nid)
|
||||
}
|
||||
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *relayQueueStatements) SelectQueueEntryCount(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
) (int64, error) {
|
||||
var count int64
|
||||
stmt := sqlutil.TxStmt(txn, s.selectQueueEntryCountStmt)
|
||||
err := stmt.QueryRowContext(ctx, serverName).Scan(&count)
|
||||
if err == sql.ErrNoRows {
|
||||
// It's acceptable for there to be no rows referencing a given
|
||||
// JSON NID but it's not an error condition. Just return as if
|
||||
// there's a zero count.
|
||||
return 0, nil
|
||||
}
|
||||
return count, err
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/shared"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// Database stores information needed by the relayapi
|
||||
type Database struct {
|
||||
shared.Database
|
||||
db *sql.DB
|
||||
writer sqlutil.Writer
|
||||
}
|
||||
|
||||
// NewDatabase opens a new database
|
||||
func NewDatabase(
|
||||
base *base.BaseDendrite,
|
||||
dbProperties *config.DatabaseOptions,
|
||||
cache caching.FederationCache,
|
||||
isLocalServerName func(gomatrixserverlib.ServerName) bool,
|
||||
) (*Database, error) {
|
||||
var d Database
|
||||
var err error
|
||||
if d.db, d.writer, err = base.DatabaseConnection(dbProperties, sqlutil.NewDummyWriter()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queue, err := NewPostgresRelayQueueTable(d.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queueJSON, err := NewPostgresRelayQueueJSONTable(d.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.Database = shared.Database{
|
||||
DB: d.db,
|
||||
IsLocalServerName: isLocalServerName,
|
||||
Cache: cache,
|
||||
Writer: d.writer,
|
||||
RelayQueue: queue,
|
||||
RelayQueueJSON: queueJSON,
|
||||
}
|
||||
return &d, nil
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/storage/shared/receipt"
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/tables"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
DB *sql.DB
|
||||
IsLocalServerName func(gomatrixserverlib.ServerName) bool
|
||||
Cache caching.FederationCache
|
||||
Writer sqlutil.Writer
|
||||
RelayQueue tables.RelayQueue
|
||||
RelayQueueJSON tables.RelayQueueJSON
|
||||
}
|
||||
|
||||
func (d *Database) StoreTransaction(
|
||||
ctx context.Context,
|
||||
transaction gomatrixserverlib.Transaction,
|
||||
) (*receipt.Receipt, error) {
|
||||
var err error
|
||||
jsonTransaction, err := json.Marshal(transaction)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal: %w", err)
|
||||
}
|
||||
|
||||
var nid int64
|
||||
_ = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
nid, err = d.RelayQueueJSON.InsertQueueJSON(ctx, txn, string(jsonTransaction))
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("d.insertQueueJSON: %w", err)
|
||||
}
|
||||
|
||||
newReceipt := receipt.NewReceipt(nid)
|
||||
return &newReceipt, nil
|
||||
}
|
||||
|
||||
func (d *Database) AssociateTransactionWithDestinations(
|
||||
ctx context.Context,
|
||||
destinations map[gomatrixserverlib.UserID]struct{},
|
||||
transactionID gomatrixserverlib.TransactionID,
|
||||
dbReceipt *receipt.Receipt,
|
||||
) error {
|
||||
err := d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
var lastErr error
|
||||
for destination := range destinations {
|
||||
destination := destination
|
||||
err := d.RelayQueue.InsertQueueEntry(
|
||||
ctx,
|
||||
txn,
|
||||
transactionID,
|
||||
destination.Domain(),
|
||||
dbReceipt.GetNID(),
|
||||
)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("d.insertQueueEntry: %w", err)
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Database) CleanTransactions(
|
||||
ctx context.Context,
|
||||
userID gomatrixserverlib.UserID,
|
||||
receipts []*receipt.Receipt,
|
||||
) error {
|
||||
nids := make([]int64, len(receipts))
|
||||
for i, dbReceipt := range receipts {
|
||||
nids[i] = dbReceipt.GetNID()
|
||||
}
|
||||
|
||||
err := d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
deleteEntryErr := d.RelayQueue.DeleteQueueEntries(ctx, txn, userID.Domain(), nids)
|
||||
// TODO : If there are still queue entries for any of these nids for other destinations
|
||||
// then we shouldn't delete the json entries.
|
||||
// But this can't happen with the current api design.
|
||||
// There will only ever be one server entry for each nid since each call to send_relay
|
||||
// only accepts a single server name and inside there we create a new json entry.
|
||||
// So for multiple destinations we would call send_relay multiple times and have multiple
|
||||
// json entries of the same transaction.
|
||||
//
|
||||
// TLDR; this works as expected right now but can easily be optimised in the future.
|
||||
deleteJSONErr := d.RelayQueueJSON.DeleteQueueJSON(ctx, txn, nids)
|
||||
|
||||
if deleteEntryErr != nil {
|
||||
return fmt.Errorf("d.deleteQueueEntries: %w", deleteEntryErr)
|
||||
}
|
||||
if deleteJSONErr != nil {
|
||||
return fmt.Errorf("d.deleteQueueJSON: %w", deleteJSONErr)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Database) GetTransaction(
|
||||
ctx context.Context,
|
||||
userID gomatrixserverlib.UserID,
|
||||
) (*gomatrixserverlib.Transaction, *receipt.Receipt, error) {
|
||||
entriesRequested := 1
|
||||
nids, err := d.RelayQueue.SelectQueueEntries(ctx, nil, userID.Domain(), entriesRequested)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("d.SelectQueueEntries: %w", err)
|
||||
}
|
||||
if len(nids) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
firstNID := nids[0]
|
||||
|
||||
txns := map[int64][]byte{}
|
||||
err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
txns, err = d.RelayQueueJSON.SelectQueueJSON(ctx, txn, nids)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("d.SelectQueueJSON: %w", err)
|
||||
}
|
||||
|
||||
transaction := &gomatrixserverlib.Transaction{}
|
||||
if _, ok := txns[firstNID]; !ok {
|
||||
return nil, nil, fmt.Errorf("Failed retrieving json blob for transaction: %d", firstNID)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(txns[firstNID], transaction)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Unmarshal transaction: %w", err)
|
||||
}
|
||||
|
||||
newReceipt := receipt.NewReceipt(firstNID)
|
||||
return transaction, &newReceipt, nil
|
||||
}
|
||||
|
||||
func (d *Database) GetTransactionCount(
|
||||
ctx context.Context,
|
||||
userID gomatrixserverlib.UserID,
|
||||
) (int64, error) {
|
||||
count, err := d.RelayQueue.SelectQueueEntryCount(ctx, nil, userID.Domain())
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("d.SelectQueueEntryCount: %w", err)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
)
|
||||
|
||||
const relayQueueJSONSchema = `
|
||||
-- The relayapi_queue_json table contains event contents that
|
||||
-- we are storing for future forwarding.
|
||||
CREATE TABLE IF NOT EXISTS relayapi_queue_json (
|
||||
-- The JSON NID. This allows cross-referencing to find the JSON blob.
|
||||
json_nid INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
-- The JSON body. Text so that we preserve UTF-8.
|
||||
json_body TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS relayapi_queue_json_json_nid_idx
|
||||
ON relayapi_queue_json (json_nid);
|
||||
`
|
||||
|
||||
const insertQueueJSONSQL = "" +
|
||||
"INSERT INTO relayapi_queue_json (json_body)" +
|
||||
" VALUES ($1)"
|
||||
|
||||
const deleteQueueJSONSQL = "" +
|
||||
"DELETE FROM relayapi_queue_json WHERE json_nid IN ($1)"
|
||||
|
||||
const selectQueueJSONSQL = "" +
|
||||
"SELECT json_nid, json_body FROM relayapi_queue_json" +
|
||||
" WHERE json_nid IN ($1)"
|
||||
|
||||
type relayQueueJSONStatements struct {
|
||||
db *sql.DB
|
||||
insertJSONStmt *sql.Stmt
|
||||
//deleteJSONStmt *sql.Stmt - prepared at runtime due to variadic
|
||||
//selectJSONStmt *sql.Stmt - prepared at runtime due to variadic
|
||||
}
|
||||
|
||||
func NewSQLiteRelayQueueJSONTable(db *sql.DB) (s *relayQueueJSONStatements, err error) {
|
||||
s = &relayQueueJSONStatements{
|
||||
db: db,
|
||||
}
|
||||
_, err = db.Exec(relayQueueJSONSchema)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return s, sqlutil.StatementList{
|
||||
{&s.insertJSONStmt, insertQueueJSONSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
func (s *relayQueueJSONStatements) InsertQueueJSON(
|
||||
ctx context.Context, txn *sql.Tx, json string,
|
||||
) (lastid int64, err error) {
|
||||
stmt := sqlutil.TxStmt(txn, s.insertJSONStmt)
|
||||
res, err := stmt.ExecContext(ctx, json)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("stmt.QueryContext: %w", err)
|
||||
}
|
||||
lastid, err = res.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("res.LastInsertId: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *relayQueueJSONStatements) DeleteQueueJSON(
|
||||
ctx context.Context, txn *sql.Tx, nids []int64,
|
||||
) error {
|
||||
deleteSQL := strings.Replace(deleteQueueJSONSQL, "($1)", sqlutil.QueryVariadic(len(nids)), 1)
|
||||
deleteStmt, err := txn.Prepare(deleteSQL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("s.deleteQueueJSON s.db.Prepare: %w", err)
|
||||
}
|
||||
|
||||
iNIDs := make([]interface{}, len(nids))
|
||||
for k, v := range nids {
|
||||
iNIDs[k] = v
|
||||
}
|
||||
|
||||
stmt := sqlutil.TxStmt(txn, deleteStmt)
|
||||
_, err = stmt.ExecContext(ctx, iNIDs...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *relayQueueJSONStatements) SelectQueueJSON(
|
||||
ctx context.Context, txn *sql.Tx, jsonNIDs []int64,
|
||||
) (map[int64][]byte, error) {
|
||||
selectSQL := strings.Replace(selectQueueJSONSQL, "($1)", sqlutil.QueryVariadic(len(jsonNIDs)), 1)
|
||||
selectStmt, err := txn.Prepare(selectSQL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("s.selectQueueJSON s.db.Prepare: %w", err)
|
||||
}
|
||||
|
||||
iNIDs := make([]interface{}, len(jsonNIDs))
|
||||
for k, v := range jsonNIDs {
|
||||
iNIDs[k] = v
|
||||
}
|
||||
|
||||
blobs := map[int64][]byte{}
|
||||
stmt := sqlutil.TxStmt(txn, selectStmt)
|
||||
rows, err := stmt.QueryContext(ctx, iNIDs...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("s.selectQueueJSON stmt.QueryContext: %w", err)
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "selectQueueJSON: rows.close() failed")
|
||||
for rows.Next() {
|
||||
var nid int64
|
||||
var blob []byte
|
||||
if err = rows.Scan(&nid, &blob); err != nil {
|
||||
return nil, fmt.Errorf("s.selectQueueJSON rows.Scan: %w", err)
|
||||
}
|
||||
blobs[nid] = blob
|
||||
}
|
||||
return blobs, err
|
||||
}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
const relayQueueSchema = `
|
||||
CREATE TABLE IF NOT EXISTS relayapi_queue (
|
||||
-- The transaction ID that was generated before persisting the event.
|
||||
transaction_id TEXT NOT NULL,
|
||||
-- The domain part of the user ID the m.room.member event is for.
|
||||
server_name TEXT NOT NULL,
|
||||
-- The JSON NID from the relayapi_queue_json table.
|
||||
json_nid BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS relayapi_queue_queue_json_nid_idx
|
||||
ON relayapi_queue (json_nid, server_name);
|
||||
CREATE INDEX IF NOT EXISTS relayapi_queue_json_nid_idx
|
||||
ON relayapi_queue (json_nid);
|
||||
CREATE INDEX IF NOT EXISTS relayapi_queue_server_name_idx
|
||||
ON relayapi_queue (server_name);
|
||||
`
|
||||
|
||||
const insertQueueEntrySQL = "" +
|
||||
"INSERT INTO relayapi_queue (transaction_id, server_name, json_nid)" +
|
||||
" VALUES ($1, $2, $3)"
|
||||
|
||||
const deleteQueueEntriesSQL = "" +
|
||||
"DELETE FROM relayapi_queue WHERE server_name = $1 AND json_nid IN ($2)"
|
||||
|
||||
const selectQueueEntriesSQL = "" +
|
||||
"SELECT json_nid FROM relayapi_queue" +
|
||||
" WHERE server_name = $1" +
|
||||
" ORDER BY json_nid" +
|
||||
" LIMIT $2"
|
||||
|
||||
const selectQueueEntryCountSQL = "" +
|
||||
"SELECT COUNT(*) FROM relayapi_queue" +
|
||||
" WHERE server_name = $1"
|
||||
|
||||
type relayQueueStatements struct {
|
||||
db *sql.DB
|
||||
insertQueueEntryStmt *sql.Stmt
|
||||
selectQueueEntriesStmt *sql.Stmt
|
||||
selectQueueEntryCountStmt *sql.Stmt
|
||||
// deleteQueueEntriesStmt *sql.Stmt - prepared at runtime due to variadic
|
||||
}
|
||||
|
||||
func NewSQLiteRelayQueueTable(
|
||||
db *sql.DB,
|
||||
) (s *relayQueueStatements, err error) {
|
||||
s = &relayQueueStatements{
|
||||
db: db,
|
||||
}
|
||||
_, err = db.Exec(relayQueueSchema)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return s, sqlutil.StatementList{
|
||||
{&s.insertQueueEntryStmt, insertQueueEntrySQL},
|
||||
{&s.selectQueueEntriesStmt, selectQueueEntriesSQL},
|
||||
{&s.selectQueueEntryCountStmt, selectQueueEntryCountSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
func (s *relayQueueStatements) InsertQueueEntry(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
transactionID gomatrixserverlib.TransactionID,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
nid int64,
|
||||
) error {
|
||||
stmt := sqlutil.TxStmt(txn, s.insertQueueEntryStmt)
|
||||
_, err := stmt.ExecContext(
|
||||
ctx,
|
||||
transactionID, // the transaction ID that we initially attempted
|
||||
serverName, // destination server name
|
||||
nid, // JSON blob NID
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *relayQueueStatements) DeleteQueueEntries(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
jsonNIDs []int64,
|
||||
) error {
|
||||
deleteSQL := strings.Replace(deleteQueueEntriesSQL, "($2)", sqlutil.QueryVariadicOffset(len(jsonNIDs), 1), 1)
|
||||
deleteStmt, err := txn.Prepare(deleteSQL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("s.deleteQueueEntries s.db.Prepare: %w", err)
|
||||
}
|
||||
|
||||
params := make([]interface{}, len(jsonNIDs)+1)
|
||||
params[0] = serverName
|
||||
for k, v := range jsonNIDs {
|
||||
params[k+1] = v
|
||||
}
|
||||
|
||||
stmt := sqlutil.TxStmt(txn, deleteStmt)
|
||||
_, err = stmt.ExecContext(ctx, params...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *relayQueueStatements) SelectQueueEntries(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
limit int,
|
||||
) ([]int64, error) {
|
||||
stmt := sqlutil.TxStmt(txn, s.selectQueueEntriesStmt)
|
||||
rows, err := stmt.QueryContext(ctx, serverName, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "queueFromStmt: rows.close() failed")
|
||||
var result []int64
|
||||
for rows.Next() {
|
||||
var nid int64
|
||||
if err = rows.Scan(&nid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, nid)
|
||||
}
|
||||
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (s *relayQueueStatements) SelectQueueEntryCount(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
) (int64, error) {
|
||||
var count int64
|
||||
stmt := sqlutil.TxStmt(txn, s.selectQueueEntryCountStmt)
|
||||
err := stmt.QueryRowContext(ctx, serverName).Scan(&count)
|
||||
if err == sql.ErrNoRows {
|
||||
// It's acceptable for there to be no rows referencing a given
|
||||
// JSON NID but it's not an error condition. Just return as if
|
||||
// there's a zero count.
|
||||
return 0, nil
|
||||
}
|
||||
return count, err
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/shared"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// Database stores information needed by the federation sender
|
||||
type Database struct {
|
||||
shared.Database
|
||||
db *sql.DB
|
||||
writer sqlutil.Writer
|
||||
}
|
||||
|
||||
// NewDatabase opens a new database
|
||||
func NewDatabase(
|
||||
base *base.BaseDendrite,
|
||||
dbProperties *config.DatabaseOptions,
|
||||
cache caching.FederationCache,
|
||||
isLocalServerName func(gomatrixserverlib.ServerName) bool,
|
||||
) (*Database, error) {
|
||||
var d Database
|
||||
var err error
|
||||
if d.db, d.writer, err = base.DatabaseConnection(dbProperties, sqlutil.NewExclusiveWriter()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queue, err := NewSQLiteRelayQueueTable(d.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queueJSON, err := NewSQLiteRelayQueueJSONTable(d.db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.Database = shared.Database{
|
||||
DB: d.db,
|
||||
IsLocalServerName: isLocalServerName,
|
||||
Cache: cache,
|
||||
Writer: d.writer,
|
||||
RelayQueue: queue,
|
||||
RelayQueueJSON: queueJSON,
|
||||
}
|
||||
return &d, nil
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !wasm
|
||||
// +build !wasm
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/postgres"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/sqlite3"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// NewDatabase opens a new database
|
||||
func NewDatabase(
|
||||
base *base.BaseDendrite,
|
||||
dbProperties *config.DatabaseOptions,
|
||||
cache caching.FederationCache,
|
||||
isLocalServerName func(gomatrixserverlib.ServerName) bool,
|
||||
) (Database, error) {
|
||||
switch {
|
||||
case dbProperties.ConnectionString.IsSQLite():
|
||||
return sqlite3.NewDatabase(base, dbProperties, cache, isLocalServerName)
|
||||
case dbProperties.ConnectionString.IsPostgres():
|
||||
return postgres.NewDatabase(base, dbProperties, cache, isLocalServerName)
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected database type")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tables
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
// RelayQueue table contains a mapping of server name to transaction id and the corresponding nid.
|
||||
// These are the transactions being stored for the given destination server.
|
||||
// The nids correspond to entries in the RelayQueueJSON table.
|
||||
type RelayQueue interface {
|
||||
// Adds a new transaction_id: server_name mapping with associated json table nid to the table.
|
||||
// Will ensure only one transaction id is present for each server_name: nid mapping.
|
||||
// Adding duplicates will silently do nothing.
|
||||
InsertQueueEntry(ctx context.Context, txn *sql.Tx, transactionID gomatrixserverlib.TransactionID, serverName gomatrixserverlib.ServerName, nid int64) error
|
||||
|
||||
// Removes multiple entries from the table corresponding the the list of nids provided.
|
||||
// If any of the provided nids don't match a row in the table, that deletion is considered
|
||||
// successful.
|
||||
DeleteQueueEntries(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, jsonNIDs []int64) error
|
||||
|
||||
// Get a list of nids associated with the provided server name.
|
||||
// Returns up to `limit` nids. The entries are returned oldest first.
|
||||
// Will return an empty list if no matches were found.
|
||||
SelectQueueEntries(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, limit int) ([]int64, error)
|
||||
|
||||
// Get the number of entries in the table associated with the provided server name.
|
||||
// If there are no matching rows, a count of 0 is returned with err set to nil.
|
||||
SelectQueueEntryCount(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName) (int64, error)
|
||||
}
|
||||
|
||||
// RelayQueueJSON table contains a map of nid to the raw transaction json.
|
||||
type RelayQueueJSON interface {
|
||||
// Adds a new transaction to the table.
|
||||
// Adding a duplicate transaction will result in a new row being added and a new unique nid.
|
||||
// return: unique nid representing this entry.
|
||||
InsertQueueJSON(ctx context.Context, txn *sql.Tx, json string) (int64, error)
|
||||
|
||||
// Removes multiple nids from the table.
|
||||
// If any of the provided nids don't match a row in the table, that deletion is considered
|
||||
// successful.
|
||||
DeleteQueueJSON(ctx context.Context, txn *sql.Tx, nids []int64) error
|
||||
|
||||
// Get the transaction json corresponding to the provided nids.
|
||||
// Will return a partial result containing any matching nid from the table.
|
||||
// Will return an empty map if no matches were found.
|
||||
// It is the caller's responsibility to deal with the results appropriately.
|
||||
// return: map indexed by nid of each matching transaction json.
|
||||
SelectQueueJSON(ctx context.Context, txn *sql.Tx, jsonNIDs []int64) (map[int64][]byte, error)
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tables_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/postgres"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/sqlite3"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/tables"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
testOrigin = gomatrixserverlib.ServerName("kaer.morhen")
|
||||
)
|
||||
|
||||
func mustCreateTransaction() gomatrixserverlib.Transaction {
|
||||
txn := gomatrixserverlib.Transaction{}
|
||||
txn.PDUs = []json.RawMessage{
|
||||
[]byte(`{"auth_events":[["$0ok8ynDp7kjc95e3:kaer.morhen",{"sha256":"sWCi6Ckp9rDimQON+MrUlNRkyfZ2tjbPbWfg2NMB18Q"}],["$LEwEu0kxrtu5fOiS:kaer.morhen",{"sha256":"1aKajq6DWHru1R1HJjvdWMEavkJJHGaTmPvfuERUXaA"}]],"content":{"body":"Test Message"},"depth":5,"event_id":"$gl2T9l3qm0kUbiIJ:kaer.morhen","hashes":{"sha256":"Qx3nRMHLDPSL5hBAzuX84FiSSP0K0Kju2iFoBWH4Za8"},"origin":"kaer.morhen","origin_server_ts":0,"prev_events":[["$UKNe10XzYzG0TeA9:kaer.morhen",{"sha256":"KtSRyMjt0ZSjsv2koixTRCxIRCGoOp6QrKscsW97XRo"}]],"room_id":"!roomid:kaer.morhen","sender":"@userid:kaer.morhen","signatures":{"kaer.morhen":{"ed25519:auto":"sqDgv3EG7ml5VREzmT9aZeBpS4gAPNIaIeJOwqjDhY0GPU/BcpX5wY4R7hYLrNe5cChgV+eFy/GWm1Zfg5FfDg"}},"type":"m.room.message"}`),
|
||||
}
|
||||
txn.Origin = testOrigin
|
||||
|
||||
return txn
|
||||
}
|
||||
|
||||
type RelayQueueJSONDatabase struct {
|
||||
DB *sql.DB
|
||||
Writer sqlutil.Writer
|
||||
Table tables.RelayQueueJSON
|
||||
}
|
||||
|
||||
func mustCreateQueueJSONTable(
|
||||
t *testing.T,
|
||||
dbType test.DBType,
|
||||
) (database RelayQueueJSONDatabase, close func()) {
|
||||
t.Helper()
|
||||
connStr, close := test.PrepareDBConnectionString(t, dbType)
|
||||
db, err := sqlutil.Open(&config.DatabaseOptions{
|
||||
ConnectionString: config.DataSource(connStr),
|
||||
}, sqlutil.NewExclusiveWriter())
|
||||
assert.NoError(t, err)
|
||||
var tab tables.RelayQueueJSON
|
||||
switch dbType {
|
||||
case test.DBTypePostgres:
|
||||
tab, err = postgres.NewPostgresRelayQueueJSONTable(db)
|
||||
assert.NoError(t, err)
|
||||
case test.DBTypeSQLite:
|
||||
tab, err = sqlite3.NewSQLiteRelayQueueJSONTable(db)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
database = RelayQueueJSONDatabase{
|
||||
DB: db,
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
Table: tab,
|
||||
}
|
||||
return database, close
|
||||
}
|
||||
|
||||
func TestShoudInsertTransaction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, close := mustCreateQueueJSONTable(t, dbType)
|
||||
defer close()
|
||||
|
||||
transaction := mustCreateTransaction()
|
||||
tx, err := json.Marshal(transaction)
|
||||
if err != nil {
|
||||
t.Fatalf("Invalid transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
_, err = db.Table.InsertQueueJSON(ctx, nil, string(tx))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestShouldRetrieveInsertedTransaction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, close := mustCreateQueueJSONTable(t, dbType)
|
||||
defer close()
|
||||
|
||||
transaction := mustCreateTransaction()
|
||||
tx, err := json.Marshal(transaction)
|
||||
if err != nil {
|
||||
t.Fatalf("Invalid transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
nid, err := db.Table.InsertQueueJSON(ctx, nil, string(tx))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
var storedJSON map[int64][]byte
|
||||
_ = db.Writer.Do(db.DB, nil, func(txn *sql.Tx) error {
|
||||
storedJSON, err = db.Table.SelectQueueJSON(ctx, txn, []int64{nid})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed retrieving transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Equal(t, 1, len(storedJSON))
|
||||
|
||||
var storedTx gomatrixserverlib.Transaction
|
||||
json.Unmarshal(storedJSON[1], &storedTx)
|
||||
|
||||
assert.Equal(t, transaction, storedTx)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShouldDeleteTransaction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, close := mustCreateQueueJSONTable(t, dbType)
|
||||
defer close()
|
||||
|
||||
transaction := mustCreateTransaction()
|
||||
tx, err := json.Marshal(transaction)
|
||||
if err != nil {
|
||||
t.Fatalf("Invalid transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
nid, err := db.Table.InsertQueueJSON(ctx, nil, string(tx))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
storedJSON := map[int64][]byte{}
|
||||
_ = db.Writer.Do(db.DB, nil, func(txn *sql.Tx) error {
|
||||
err = db.Table.DeleteQueueJSON(ctx, txn, []int64{nid})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed deleting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
storedJSON = map[int64][]byte{}
|
||||
_ = db.Writer.Do(db.DB, nil, func(txn *sql.Tx) error {
|
||||
storedJSON, err = db.Table.SelectQueueJSON(ctx, txn, []int64{nid})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed retrieving transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, len(storedJSON))
|
||||
})
|
||||
}
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tables_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/postgres"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/sqlite3"
|
||||
"github.com/matrix-org/dendrite/relayapi/storage/tables"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/test"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type RelayQueueDatabase struct {
|
||||
DB *sql.DB
|
||||
Writer sqlutil.Writer
|
||||
Table tables.RelayQueue
|
||||
}
|
||||
|
||||
func mustCreateQueueTable(
|
||||
t *testing.T,
|
||||
dbType test.DBType,
|
||||
) (database RelayQueueDatabase, close func()) {
|
||||
t.Helper()
|
||||
connStr, close := test.PrepareDBConnectionString(t, dbType)
|
||||
db, err := sqlutil.Open(&config.DatabaseOptions{
|
||||
ConnectionString: config.DataSource(connStr),
|
||||
}, sqlutil.NewExclusiveWriter())
|
||||
assert.NoError(t, err)
|
||||
var tab tables.RelayQueue
|
||||
switch dbType {
|
||||
case test.DBTypePostgres:
|
||||
tab, err = postgres.NewPostgresRelayQueueTable(db)
|
||||
assert.NoError(t, err)
|
||||
case test.DBTypeSQLite:
|
||||
tab, err = sqlite3.NewSQLiteRelayQueueTable(db)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
database = RelayQueueDatabase{
|
||||
DB: db,
|
||||
Writer: sqlutil.NewDummyWriter(),
|
||||
Table: tab,
|
||||
}
|
||||
return database, close
|
||||
}
|
||||
|
||||
func TestShoudInsertQueueTransaction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, close := mustCreateQueueTable(t, dbType)
|
||||
defer close()
|
||||
|
||||
transactionID := gomatrixserverlib.TransactionID(fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
serverName := gomatrixserverlib.ServerName("domain")
|
||||
nid := int64(1)
|
||||
err := db.Table.InsertQueueEntry(ctx, nil, transactionID, serverName, nid)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestShouldRetrieveInsertedQueueTransaction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, close := mustCreateQueueTable(t, dbType)
|
||||
defer close()
|
||||
|
||||
transactionID := gomatrixserverlib.TransactionID(fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
serverName := gomatrixserverlib.ServerName("domain")
|
||||
nid := int64(1)
|
||||
|
||||
err := db.Table.InsertQueueEntry(ctx, nil, transactionID, serverName, nid)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
retrievedNids, err := db.Table.SelectQueueEntries(ctx, nil, serverName, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed retrieving transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Equal(t, nid, retrievedNids[0])
|
||||
assert.Equal(t, 1, len(retrievedNids))
|
||||
})
|
||||
}
|
||||
|
||||
func TestShouldRetrieveOldestInsertedQueueTransaction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, close := mustCreateQueueTable(t, dbType)
|
||||
defer close()
|
||||
|
||||
transactionID := gomatrixserverlib.TransactionID(fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
serverName := gomatrixserverlib.ServerName("domain")
|
||||
nid := int64(2)
|
||||
err := db.Table.InsertQueueEntry(ctx, nil, transactionID, serverName, nid)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
transactionID = gomatrixserverlib.TransactionID(fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
serverName = gomatrixserverlib.ServerName("domain")
|
||||
oldestNID := int64(1)
|
||||
err = db.Table.InsertQueueEntry(ctx, nil, transactionID, serverName, oldestNID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
retrievedNids, err := db.Table.SelectQueueEntries(ctx, nil, serverName, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed retrieving transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Equal(t, oldestNID, retrievedNids[0])
|
||||
assert.Equal(t, 1, len(retrievedNids))
|
||||
|
||||
retrievedNids, err = db.Table.SelectQueueEntries(ctx, nil, serverName, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed retrieving transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Equal(t, oldestNID, retrievedNids[0])
|
||||
assert.Equal(t, nid, retrievedNids[1])
|
||||
assert.Equal(t, 2, len(retrievedNids))
|
||||
})
|
||||
}
|
||||
|
||||
func TestShouldDeleteQueueTransaction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, close := mustCreateQueueTable(t, dbType)
|
||||
defer close()
|
||||
|
||||
transactionID := gomatrixserverlib.TransactionID(fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
serverName := gomatrixserverlib.ServerName("domain")
|
||||
nid := int64(1)
|
||||
|
||||
err := db.Table.InsertQueueEntry(ctx, nil, transactionID, serverName, nid)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
_ = db.Writer.Do(db.DB, nil, func(txn *sql.Tx) error {
|
||||
err = db.Table.DeleteQueueEntries(ctx, txn, serverName, []int64{nid})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed deleting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
count, err := db.Table.SelectQueueEntryCount(ctx, nil, serverName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed retrieving transaction count: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, int64(0), count)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShouldDeleteOnlySpecifiedQueueTransaction(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
db, close := mustCreateQueueTable(t, dbType)
|
||||
defer close()
|
||||
|
||||
transactionID := gomatrixserverlib.TransactionID(fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
serverName := gomatrixserverlib.ServerName("domain")
|
||||
nid := int64(1)
|
||||
transactionID2 := gomatrixserverlib.TransactionID(fmt.Sprintf("%d2", time.Now().UnixNano()))
|
||||
serverName2 := gomatrixserverlib.ServerName("domain2")
|
||||
nid2 := int64(2)
|
||||
transactionID3 := gomatrixserverlib.TransactionID(fmt.Sprintf("%d3", time.Now().UnixNano()))
|
||||
|
||||
err := db.Table.InsertQueueEntry(ctx, nil, transactionID, serverName, nid)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
err = db.Table.InsertQueueEntry(ctx, nil, transactionID2, serverName2, nid)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
err = db.Table.InsertQueueEntry(ctx, nil, transactionID3, serverName, nid2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed inserting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
_ = db.Writer.Do(db.DB, nil, func(txn *sql.Tx) error {
|
||||
err = db.Table.DeleteQueueEntries(ctx, txn, serverName, []int64{nid})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed deleting transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
count, err := db.Table.SelectQueueEntryCount(ctx, nil, serverName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed retrieving transaction count: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
count, err = db.Table.SelectQueueEntryCount(ctx, nil, serverName2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed retrieving transaction count: %s", err.Error())
|
||||
}
|
||||
assert.Equal(t, int64(1), count)
|
||||
})
|
||||
}
|
||||
|
|
@ -62,7 +62,6 @@ type Dendrite struct {
|
|||
RoomServer RoomServer `yaml:"room_server"`
|
||||
SyncAPI SyncAPI `yaml:"sync_api"`
|
||||
UserAPI UserAPI `yaml:"user_api"`
|
||||
RelayAPI RelayAPI `yaml:"relay_api"`
|
||||
|
||||
MSCs MSCs `yaml:"mscs"`
|
||||
|
||||
|
|
@ -335,7 +334,6 @@ func (c *Dendrite) Defaults(opts DefaultOpts) {
|
|||
c.SyncAPI.Defaults(opts)
|
||||
c.UserAPI.Defaults(opts)
|
||||
c.AppServiceAPI.Defaults(opts)
|
||||
c.RelayAPI.Defaults(opts)
|
||||
c.MSCs.Defaults(opts)
|
||||
c.Wiring()
|
||||
}
|
||||
|
|
@ -348,7 +346,7 @@ func (c *Dendrite) Verify(configErrs *ConfigErrors) {
|
|||
&c.Global, &c.ClientAPI, &c.FederationAPI,
|
||||
&c.KeyServer, &c.MediaAPI, &c.RoomServer,
|
||||
&c.SyncAPI, &c.UserAPI,
|
||||
&c.AppServiceAPI, &c.RelayAPI, &c.MSCs,
|
||||
&c.AppServiceAPI, &c.MSCs,
|
||||
} {
|
||||
c.Verify(configErrs)
|
||||
}
|
||||
|
|
@ -364,7 +362,6 @@ func (c *Dendrite) Wiring() {
|
|||
c.SyncAPI.Matrix = &c.Global
|
||||
c.UserAPI.Matrix = &c.Global
|
||||
c.AppServiceAPI.Matrix = &c.Global
|
||||
c.RelayAPI.Matrix = &c.Global
|
||||
c.MSCs.Matrix = &c.Global
|
||||
|
||||
c.ClientAPI.Derived = &c.Derived
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
|
||||
type RelayAPI struct {
|
||||
Matrix *Global `yaml:"-"`
|
||||
|
||||
// The database stores information used by the relay queue to
|
||||
// forward transactions to remote servers.
|
||||
Database DatabaseOptions `yaml:"database,omitempty"`
|
||||
}
|
||||
|
||||
func (c *RelayAPI) Defaults(opts DefaultOpts) {
|
||||
if opts.Generate {
|
||||
if !opts.SingleDatabase {
|
||||
c.Database.ConnectionString = "file:relayapi.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RelayAPI) Verify(configErrs *ConfigErrors) {
|
||||
if c.Matrix.DatabaseOptions.ConnectionString == "" {
|
||||
checkNotEmpty(configErrs, "relay_api.database.connection_string", string(c.Database.ConnectionString))
|
||||
}
|
||||
}
|
||||
|
|
@ -22,8 +22,6 @@ import (
|
|||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
"github.com/matrix-org/dendrite/internal/transactions"
|
||||
"github.com/matrix-org/dendrite/mediaapi"
|
||||
"github.com/matrix-org/dendrite/relayapi"
|
||||
relayAPI "github.com/matrix-org/dendrite/relayapi/api"
|
||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
|
|
@ -44,7 +42,6 @@ type Monolith struct {
|
|||
FederationAPI federationAPI.FederationInternalAPI
|
||||
RoomserverAPI roomserverAPI.RoomserverInternalAPI
|
||||
UserAPI userapi.UserInternalAPI
|
||||
RelayAPI relayAPI.RelayInternalAPI
|
||||
|
||||
// Optional
|
||||
ExtPublicRoomsProvider api.ExtraPublicRoomsProvider
|
||||
|
|
@ -68,7 +65,4 @@ func (m *Monolith) AddAllPublicRoutes(base *base.BaseDendrite) {
|
|||
mediaapi.AddPublicRoutes(base, m.UserAPI, m.Client)
|
||||
syncapi.AddPublicRoutes(base, m.UserAPI, m.RoomserverAPI)
|
||||
|
||||
if m.RelayAPI != nil {
|
||||
relayapi.AddPublicRoutes(base, m.KeyRing, m.RelayAPI)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ func CreateBaseDendrite(t *testing.T, dbType test.DBType) (*base.BaseDendrite, f
|
|||
cfg.RoomServer.Database.ConnectionString = config.DataSource(filepath.Join("file://", tempDir, "roomserver.db"))
|
||||
cfg.SyncAPI.Database.ConnectionString = config.DataSource(filepath.Join("file://", tempDir, "syncapi.db"))
|
||||
cfg.UserAPI.AccountDatabase.ConnectionString = config.DataSource(filepath.Join("file://", tempDir, "userapi.db"))
|
||||
cfg.RelayAPI.Database.ConnectionString = config.DataSource(filepath.Join("file://", tempDir, "relayapi.db"))
|
||||
|
||||
base := base.NewBaseDendrite(&cfg, base.DisableMetrics)
|
||||
return base, func() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue