mirror of
https://github.com/matrix-org/dendrite.git
synced 2024-12-04 20:21:56 -06:00
c45d0936b5
* Generic-based internal HTTP API (tested out on a few endpoints in the federation API)
* Add `PerformInvite`
* More tweaks
* Fix metric name
* Fix LookupStateIDs
* Lots of changes to clients
* Some serverside stuff
* Some error handling
* Use paths as metric names
* Revert "Use paths as metric names"
This reverts commit a9323a6a34
.
* Namespace metric names
* Remove duplicate entry
* Remove another duplicate entry
* Tweak error handling
* Some more tweaks
* Update error behaviour
* Some more error tweaking
* Fix API path for `PerformDeleteKeys`
* Fix another path
* Tweak federation client proxying
* Fix another path
* Don't return typed nils
* Some more tweaks, not that it makes any difference
* Tweak federation client proxying
* Maybe fix the key backup test
478 lines
16 KiB
Go
478 lines
16 KiB
Go
package inthttp
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/matrix-org/dendrite/federationapi/api"
|
|
"github.com/matrix-org/dendrite/internal/caching"
|
|
"github.com/matrix-org/dendrite/internal/httputil"
|
|
"github.com/matrix-org/gomatrix"
|
|
"github.com/matrix-org/gomatrixserverlib"
|
|
)
|
|
|
|
// HTTP paths for the internal HTTP API
|
|
const (
|
|
FederationAPIQueryJoinedHostServerNamesInRoomPath = "/federationapi/queryJoinedHostServerNamesInRoom"
|
|
FederationAPIQueryServerKeysPath = "/federationapi/queryServerKeys"
|
|
|
|
FederationAPIPerformDirectoryLookupRequestPath = "/federationapi/performDirectoryLookup"
|
|
FederationAPIPerformJoinRequestPath = "/federationapi/performJoinRequest"
|
|
FederationAPIPerformLeaveRequestPath = "/federationapi/performLeaveRequest"
|
|
FederationAPIPerformInviteRequestPath = "/federationapi/performInviteRequest"
|
|
FederationAPIPerformOutboundPeekRequestPath = "/federationapi/performOutboundPeekRequest"
|
|
FederationAPIPerformBroadcastEDUPath = "/federationapi/performBroadcastEDU"
|
|
|
|
FederationAPIGetUserDevicesPath = "/federationapi/client/getUserDevices"
|
|
FederationAPIClaimKeysPath = "/federationapi/client/claimKeys"
|
|
FederationAPIQueryKeysPath = "/federationapi/client/queryKeys"
|
|
FederationAPIBackfillPath = "/federationapi/client/backfill"
|
|
FederationAPILookupStatePath = "/federationapi/client/lookupState"
|
|
FederationAPILookupStateIDsPath = "/federationapi/client/lookupStateIDs"
|
|
FederationAPILookupMissingEventsPath = "/federationapi/client/lookupMissingEvents"
|
|
FederationAPIGetEventPath = "/federationapi/client/getEvent"
|
|
FederationAPILookupServerKeysPath = "/federationapi/client/lookupServerKeys"
|
|
FederationAPIEventRelationshipsPath = "/federationapi/client/msc2836eventRelationships"
|
|
FederationAPISpacesSummaryPath = "/federationapi/client/msc2946spacesSummary"
|
|
FederationAPIGetEventAuthPath = "/federationapi/client/getEventAuth"
|
|
|
|
FederationAPIInputPublicKeyPath = "/federationapi/inputPublicKey"
|
|
FederationAPIQueryPublicKeyPath = "/federationapi/queryPublicKey"
|
|
)
|
|
|
|
// NewFederationAPIClient creates a FederationInternalAPI implemented by talking to a HTTP POST API.
|
|
// If httpClient is nil an error is returned
|
|
func NewFederationAPIClient(federationSenderURL string, httpClient *http.Client, cache caching.ServerKeyCache) (api.FederationInternalAPI, error) {
|
|
if httpClient == nil {
|
|
return nil, errors.New("NewFederationInternalAPIHTTP: httpClient is <nil>")
|
|
}
|
|
return &httpFederationInternalAPI{
|
|
federationAPIURL: federationSenderURL,
|
|
httpClient: httpClient,
|
|
cache: cache,
|
|
}, nil
|
|
}
|
|
|
|
type httpFederationInternalAPI struct {
|
|
federationAPIURL string
|
|
httpClient *http.Client
|
|
cache caching.ServerKeyCache
|
|
}
|
|
|
|
// Handle an instruction to make_leave & send_leave with a remote server.
|
|
func (h *httpFederationInternalAPI) PerformLeave(
|
|
ctx context.Context,
|
|
request *api.PerformLeaveRequest,
|
|
response *api.PerformLeaveResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"PerformLeave", h.federationAPIURL+FederationAPIPerformLeaveRequestPath,
|
|
h.httpClient, ctx, request, response,
|
|
)
|
|
}
|
|
|
|
// Handle sending an invite to a remote server.
|
|
func (h *httpFederationInternalAPI) PerformInvite(
|
|
ctx context.Context,
|
|
request *api.PerformInviteRequest,
|
|
response *api.PerformInviteResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"PerformInvite", h.federationAPIURL+FederationAPIPerformInviteRequestPath,
|
|
h.httpClient, ctx, request, response,
|
|
)
|
|
}
|
|
|
|
// Handle starting a peek on a remote server.
|
|
func (h *httpFederationInternalAPI) PerformOutboundPeek(
|
|
ctx context.Context,
|
|
request *api.PerformOutboundPeekRequest,
|
|
response *api.PerformOutboundPeekResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"PerformOutboundPeek", h.federationAPIURL+FederationAPIPerformOutboundPeekRequestPath,
|
|
h.httpClient, ctx, request, response,
|
|
)
|
|
}
|
|
|
|
// QueryJoinedHostServerNamesInRoom implements FederationInternalAPI
|
|
func (h *httpFederationInternalAPI) QueryJoinedHostServerNamesInRoom(
|
|
ctx context.Context,
|
|
request *api.QueryJoinedHostServerNamesInRoomRequest,
|
|
response *api.QueryJoinedHostServerNamesInRoomResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"QueryJoinedHostServerNamesInRoom", h.federationAPIURL+FederationAPIQueryJoinedHostServerNamesInRoomPath,
|
|
h.httpClient, ctx, request, response,
|
|
)
|
|
}
|
|
|
|
// Handle an instruction to make_join & send_join with a remote server.
|
|
func (h *httpFederationInternalAPI) PerformJoin(
|
|
ctx context.Context,
|
|
request *api.PerformJoinRequest,
|
|
response *api.PerformJoinResponse,
|
|
) {
|
|
if err := httputil.CallInternalRPCAPI(
|
|
"PerformJoinRequest", h.federationAPIURL+FederationAPIPerformJoinRequestPath,
|
|
h.httpClient, ctx, request, response,
|
|
); err != nil {
|
|
response.LastError = &gomatrix.HTTPError{
|
|
Message: err.Error(),
|
|
Code: 0,
|
|
WrappedError: err,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle an instruction to make_join & send_join with a remote server.
|
|
func (h *httpFederationInternalAPI) PerformDirectoryLookup(
|
|
ctx context.Context,
|
|
request *api.PerformDirectoryLookupRequest,
|
|
response *api.PerformDirectoryLookupResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"PerformDirectoryLookup", h.federationAPIURL+FederationAPIPerformDirectoryLookupRequestPath,
|
|
h.httpClient, ctx, request, response,
|
|
)
|
|
}
|
|
|
|
// Handle an instruction to broadcast an EDU to all servers in rooms we are joined to.
|
|
func (h *httpFederationInternalAPI) PerformBroadcastEDU(
|
|
ctx context.Context,
|
|
request *api.PerformBroadcastEDURequest,
|
|
response *api.PerformBroadcastEDUResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"PerformBroadcastEDU", h.federationAPIURL+FederationAPIPerformBroadcastEDUPath,
|
|
h.httpClient, ctx, request, response,
|
|
)
|
|
}
|
|
|
|
type getUserDevices struct {
|
|
S gomatrixserverlib.ServerName
|
|
UserID string
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) GetUserDevices(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, userID string,
|
|
) (gomatrixserverlib.RespUserDevices, error) {
|
|
return httputil.CallInternalProxyAPI[getUserDevices, gomatrixserverlib.RespUserDevices, *api.FederationClientError](
|
|
"GetUserDevices", h.federationAPIURL+FederationAPIGetUserDevicesPath, h.httpClient,
|
|
ctx, &getUserDevices{
|
|
S: s,
|
|
UserID: userID,
|
|
},
|
|
)
|
|
}
|
|
|
|
type claimKeys struct {
|
|
S gomatrixserverlib.ServerName
|
|
OneTimeKeys map[string]map[string]string
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) ClaimKeys(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string,
|
|
) (gomatrixserverlib.RespClaimKeys, error) {
|
|
return httputil.CallInternalProxyAPI[claimKeys, gomatrixserverlib.RespClaimKeys, *api.FederationClientError](
|
|
"ClaimKeys", h.federationAPIURL+FederationAPIClaimKeysPath, h.httpClient,
|
|
ctx, &claimKeys{
|
|
S: s,
|
|
OneTimeKeys: oneTimeKeys,
|
|
},
|
|
)
|
|
}
|
|
|
|
type queryKeys struct {
|
|
S gomatrixserverlib.ServerName
|
|
Keys map[string][]string
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) QueryKeys(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, keys map[string][]string,
|
|
) (gomatrixserverlib.RespQueryKeys, error) {
|
|
return httputil.CallInternalProxyAPI[queryKeys, gomatrixserverlib.RespQueryKeys, *api.FederationClientError](
|
|
"QueryKeys", h.federationAPIURL+FederationAPIQueryKeysPath, h.httpClient,
|
|
ctx, &queryKeys{
|
|
S: s,
|
|
Keys: keys,
|
|
},
|
|
)
|
|
}
|
|
|
|
type backfill struct {
|
|
S gomatrixserverlib.ServerName
|
|
RoomID string
|
|
Limit int
|
|
EventIDs []string
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) Backfill(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, roomID string, limit int, eventIDs []string,
|
|
) (gomatrixserverlib.Transaction, error) {
|
|
return httputil.CallInternalProxyAPI[backfill, gomatrixserverlib.Transaction, *api.FederationClientError](
|
|
"Backfill", h.federationAPIURL+FederationAPIBackfillPath, h.httpClient,
|
|
ctx, &backfill{
|
|
S: s,
|
|
RoomID: roomID,
|
|
Limit: limit,
|
|
EventIDs: eventIDs,
|
|
},
|
|
)
|
|
}
|
|
|
|
type lookupState struct {
|
|
S gomatrixserverlib.ServerName
|
|
RoomID string
|
|
EventID string
|
|
RoomVersion gomatrixserverlib.RoomVersion
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) LookupState(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion,
|
|
) (gomatrixserverlib.RespState, error) {
|
|
return httputil.CallInternalProxyAPI[lookupState, gomatrixserverlib.RespState, *api.FederationClientError](
|
|
"LookupState", h.federationAPIURL+FederationAPILookupStatePath, h.httpClient,
|
|
ctx, &lookupState{
|
|
S: s,
|
|
RoomID: roomID,
|
|
EventID: eventID,
|
|
RoomVersion: roomVersion,
|
|
},
|
|
)
|
|
}
|
|
|
|
type lookupStateIDs struct {
|
|
S gomatrixserverlib.ServerName
|
|
RoomID string
|
|
EventID string
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) LookupStateIDs(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, roomID, eventID string,
|
|
) (gomatrixserverlib.RespStateIDs, error) {
|
|
return httputil.CallInternalProxyAPI[lookupStateIDs, gomatrixserverlib.RespStateIDs, *api.FederationClientError](
|
|
"LookupStateIDs", h.federationAPIURL+FederationAPILookupStateIDsPath, h.httpClient,
|
|
ctx, &lookupStateIDs{
|
|
S: s,
|
|
RoomID: roomID,
|
|
EventID: eventID,
|
|
},
|
|
)
|
|
}
|
|
|
|
type lookupMissingEvents struct {
|
|
S gomatrixserverlib.ServerName
|
|
RoomID string
|
|
Missing gomatrixserverlib.MissingEvents
|
|
RoomVersion gomatrixserverlib.RoomVersion
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) LookupMissingEvents(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, roomID string,
|
|
missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion,
|
|
) (res gomatrixserverlib.RespMissingEvents, err error) {
|
|
return httputil.CallInternalProxyAPI[lookupMissingEvents, gomatrixserverlib.RespMissingEvents, *api.FederationClientError](
|
|
"LookupMissingEvents", h.federationAPIURL+FederationAPILookupMissingEventsPath, h.httpClient,
|
|
ctx, &lookupMissingEvents{
|
|
S: s,
|
|
RoomID: roomID,
|
|
Missing: missing,
|
|
RoomVersion: roomVersion,
|
|
},
|
|
)
|
|
}
|
|
|
|
type getEvent struct {
|
|
S gomatrixserverlib.ServerName
|
|
EventID string
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) GetEvent(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, eventID string,
|
|
) (gomatrixserverlib.Transaction, error) {
|
|
return httputil.CallInternalProxyAPI[getEvent, gomatrixserverlib.Transaction, *api.FederationClientError](
|
|
"GetEvent", h.federationAPIURL+FederationAPIGetEventPath, h.httpClient,
|
|
ctx, &getEvent{
|
|
S: s,
|
|
EventID: eventID,
|
|
},
|
|
)
|
|
}
|
|
|
|
type getEventAuth struct {
|
|
S gomatrixserverlib.ServerName
|
|
RoomVersion gomatrixserverlib.RoomVersion
|
|
RoomID string
|
|
EventID string
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) GetEventAuth(
|
|
ctx context.Context, s gomatrixserverlib.ServerName,
|
|
roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string,
|
|
) (gomatrixserverlib.RespEventAuth, error) {
|
|
return httputil.CallInternalProxyAPI[getEventAuth, gomatrixserverlib.RespEventAuth, *api.FederationClientError](
|
|
"GetEventAuth", h.federationAPIURL+FederationAPIGetEventAuthPath, h.httpClient,
|
|
ctx, &getEventAuth{
|
|
S: s,
|
|
RoomVersion: roomVersion,
|
|
RoomID: roomID,
|
|
EventID: eventID,
|
|
},
|
|
)
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) QueryServerKeys(
|
|
ctx context.Context, req *api.QueryServerKeysRequest, res *api.QueryServerKeysResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"QueryServerKeys", h.federationAPIURL+FederationAPIQueryServerKeysPath,
|
|
h.httpClient, ctx, req, res,
|
|
)
|
|
}
|
|
|
|
type lookupServerKeys struct {
|
|
S gomatrixserverlib.ServerName
|
|
KeyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) LookupServerKeys(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
|
) ([]gomatrixserverlib.ServerKeys, error) {
|
|
return httputil.CallInternalProxyAPI[lookupServerKeys, []gomatrixserverlib.ServerKeys, *api.FederationClientError](
|
|
"LookupServerKeys", h.federationAPIURL+FederationAPILookupServerKeysPath, h.httpClient,
|
|
ctx, &lookupServerKeys{
|
|
S: s,
|
|
KeyRequests: keyRequests,
|
|
},
|
|
)
|
|
}
|
|
|
|
type eventRelationships struct {
|
|
S gomatrixserverlib.ServerName
|
|
Req gomatrixserverlib.MSC2836EventRelationshipsRequest
|
|
RoomVer gomatrixserverlib.RoomVersion
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) MSC2836EventRelationships(
|
|
ctx context.Context, s gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest,
|
|
roomVersion gomatrixserverlib.RoomVersion,
|
|
) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error) {
|
|
return httputil.CallInternalProxyAPI[eventRelationships, gomatrixserverlib.MSC2836EventRelationshipsResponse, *api.FederationClientError](
|
|
"MSC2836EventRelationships", h.federationAPIURL+FederationAPIEventRelationshipsPath, h.httpClient,
|
|
ctx, &eventRelationships{
|
|
S: s,
|
|
Req: r,
|
|
RoomVer: roomVersion,
|
|
},
|
|
)
|
|
}
|
|
|
|
type spacesReq struct {
|
|
S gomatrixserverlib.ServerName
|
|
SuggestedOnly bool
|
|
RoomID string
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) MSC2946Spaces(
|
|
ctx context.Context, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool,
|
|
) (res gomatrixserverlib.MSC2946SpacesResponse, err error) {
|
|
return httputil.CallInternalProxyAPI[spacesReq, gomatrixserverlib.MSC2946SpacesResponse, *api.FederationClientError](
|
|
"MSC2836EventRelationships", h.federationAPIURL+FederationAPISpacesSummaryPath, h.httpClient,
|
|
ctx, &spacesReq{
|
|
S: dst,
|
|
SuggestedOnly: suggestedOnly,
|
|
RoomID: roomID,
|
|
},
|
|
)
|
|
}
|
|
|
|
func (s *httpFederationInternalAPI) KeyRing() *gomatrixserverlib.KeyRing {
|
|
// This is a bit of a cheat - we tell gomatrixserverlib that this API is
|
|
// both the key database and the key fetcher. While this does have the
|
|
// rather unfortunate effect of preventing gomatrixserverlib from handling
|
|
// key fetchers directly, we can at least reimplement this behaviour on
|
|
// the other end of the API.
|
|
return &gomatrixserverlib.KeyRing{
|
|
KeyDatabase: s,
|
|
KeyFetchers: []gomatrixserverlib.KeyFetcher{},
|
|
}
|
|
}
|
|
|
|
func (s *httpFederationInternalAPI) FetcherName() string {
|
|
return "httpServerKeyInternalAPI"
|
|
}
|
|
|
|
func (s *httpFederationInternalAPI) StoreKeys(
|
|
_ context.Context,
|
|
results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult,
|
|
) error {
|
|
// Run in a background context - we don't want to stop this work just
|
|
// because the caller gives up waiting.
|
|
ctx := context.Background()
|
|
request := api.InputPublicKeysRequest{
|
|
Keys: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult),
|
|
}
|
|
response := api.InputPublicKeysResponse{}
|
|
for req, res := range results {
|
|
request.Keys[req] = res
|
|
s.cache.StoreServerKey(req, res)
|
|
}
|
|
return s.InputPublicKeys(ctx, &request, &response)
|
|
}
|
|
|
|
func (s *httpFederationInternalAPI) FetchKeys(
|
|
_ context.Context,
|
|
requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp,
|
|
) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) {
|
|
// Run in a background context - we don't want to stop this work just
|
|
// because the caller gives up waiting.
|
|
ctx := context.Background()
|
|
result := make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult)
|
|
request := api.QueryPublicKeysRequest{
|
|
Requests: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp),
|
|
}
|
|
response := api.QueryPublicKeysResponse{
|
|
Results: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult),
|
|
}
|
|
for req, ts := range requests {
|
|
if res, ok := s.cache.GetServerKey(req, ts); ok {
|
|
result[req] = res
|
|
continue
|
|
}
|
|
request.Requests[req] = ts
|
|
}
|
|
err := s.QueryPublicKeys(ctx, &request, &response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for req, res := range response.Results {
|
|
result[req] = res
|
|
s.cache.StoreServerKey(req, res)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) InputPublicKeys(
|
|
ctx context.Context,
|
|
request *api.InputPublicKeysRequest,
|
|
response *api.InputPublicKeysResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"InputPublicKey", h.federationAPIURL+FederationAPIInputPublicKeyPath,
|
|
h.httpClient, ctx, request, response,
|
|
)
|
|
}
|
|
|
|
func (h *httpFederationInternalAPI) QueryPublicKeys(
|
|
ctx context.Context,
|
|
request *api.QueryPublicKeysRequest,
|
|
response *api.QueryPublicKeysResponse,
|
|
) error {
|
|
return httputil.CallInternalRPCAPI(
|
|
"QueryPublicKeys", h.federationAPIURL+FederationAPIQueryPublicKeyPath,
|
|
h.httpClient, ctx, request, response,
|
|
)
|
|
}
|