Generic-based internal HTTP API (tested out on a few endpoints in the federation API)

This commit is contained in:
Neil Alexander 2022-08-05 15:29:08 +01:00
parent 3a156a434a
commit 42b9cbcce7
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
3 changed files with 123 additions and 85 deletions

View file

@ -48,13 +48,31 @@ func NewFederationAPIClient(federationSenderURL string, httpClient *http.Client,
if httpClient == nil {
return nil, errors.New("NewFederationInternalAPIHTTP: httpClient is <nil>")
}
return &httpFederationInternalAPI{federationSenderURL, httpClient, cache}, nil
return &httpFederationInternalAPI{
federationAPIURL: federationSenderURL,
httpClient: httpClient,
cache: cache,
performLeave: httputil.NewInternalAPIClient[api.PerformLeaveRequest, api.PerformLeaveResponse](
"PerformLeave", federationSenderURL+FederationAPIPerformLeaveRequestPath, httpClient,
),
performInvite: httputil.NewInternalAPIClient[api.PerformInviteRequest, api.PerformInviteResponse](
"PerformInvite", federationSenderURL+FederationAPIPerformInviteRequestPath, httpClient,
),
performOutboundPeek: httputil.NewInternalAPIClient[api.PerformOutboundPeekRequest, api.PerformOutboundPeekResponse](
"PerformOutboundPeek", federationSenderURL+FederationAPIPerformOutboundPeekRequestPath, httpClient,
),
}, nil
}
type httpFederationInternalAPI struct {
federationAPIURL string
httpClient *http.Client
cache caching.ServerKeyCache
performLeave *httputil.InternalAPIClient[api.PerformLeaveRequest, api.PerformLeaveResponse]
performInvite *httputil.InternalAPIClient[api.PerformInviteRequest, api.PerformInviteResponse]
performOutboundPeek *httputil.InternalAPIClient[api.PerformOutboundPeekRequest, api.PerformOutboundPeekResponse]
}
// Handle an instruction to make_leave & send_leave with a remote server.
@ -63,11 +81,7 @@ func (h *httpFederationInternalAPI) PerformLeave(
request *api.PerformLeaveRequest,
response *api.PerformLeaveResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformLeaveRequest")
defer span.Finish()
apiURL := h.federationAPIURL + FederationAPIPerformLeaveRequestPath
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
return h.performLeave.Call(ctx, request, response)
}
// Handle sending an invite to a remote server.
@ -76,11 +90,7 @@ func (h *httpFederationInternalAPI) PerformInvite(
request *api.PerformInviteRequest,
response *api.PerformInviteResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformInviteRequest")
defer span.Finish()
apiURL := h.federationAPIURL + FederationAPIPerformInviteRequestPath
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
return h.performInvite.Call(ctx, request, response)
}
// Handle starting a peek on a remote server.
@ -89,11 +99,7 @@ func (h *httpFederationInternalAPI) PerformOutboundPeek(
request *api.PerformOutboundPeekRequest,
response *api.PerformOutboundPeekResponse,
) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformOutboundPeekRequest")
defer span.Finish()
apiURL := h.federationAPIURL + FederationAPIPerformOutboundPeekRequestPath
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
return h.performOutboundPeek.Call(ctx, request, response)
}
// QueryJoinedHostServerNamesInRoom implements FederationInternalAPI

View file

@ -13,20 +13,30 @@ import (
// AddRoutes adds the FederationInternalAPI handlers to the http.ServeMux.
// nolint:gocyclo
func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
internalAPIMux.Handle(
httputil.NewInternalAPIServer(
"QueryJoinedHostServerNamesInRoom",
FederationAPIQueryJoinedHostServerNamesInRoomPath,
httputil.MakeInternalAPI("QueryJoinedHostServerNamesInRoom", func(req *http.Request) util.JSONResponse {
var request api.QueryJoinedHostServerNamesInRoomRequest
var response api.QueryJoinedHostServerNamesInRoomResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.ErrorResponse(err)
}
if err := intAPI.QueryJoinedHostServerNamesInRoom(req.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
intAPI.QueryJoinedHostServerNamesInRoom,
).Serve(internalAPIMux)
httputil.NewInternalAPIServer(
"PerformLeave",
FederationAPIPerformLeaveRequestPath,
intAPI.PerformLeave,
).Serve(internalAPIMux)
httputil.NewInternalAPIServer(
"PerformDirectoryLookupRequest",
FederationAPIPerformDirectoryLookupRequestPath,
intAPI.PerformDirectoryLookup,
).Serve(internalAPIMux)
httputil.NewInternalAPIServer(
"PerformBroadcastEDU",
FederationAPIPerformBroadcastEDUPath,
intAPI.PerformBroadcastEDU,
).Serve(internalAPIMux)
internalAPIMux.Handle(
FederationAPIPerformJoinRequestPath,
httputil.MakeInternalAPI("PerformJoinRequest", func(req *http.Request) util.JSONResponse {
@ -39,62 +49,7 @@ func AddRoutes(intAPI api.FederationInternalAPI, internalAPIMux *mux.Router) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
internalAPIMux.Handle(
FederationAPIPerformLeaveRequestPath,
httputil.MakeInternalAPI("PerformLeaveRequest", func(req *http.Request) util.JSONResponse {
var request api.PerformLeaveRequest
var response api.PerformLeaveResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error())
}
if err := intAPI.PerformLeave(req.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
internalAPIMux.Handle(
FederationAPIPerformInviteRequestPath,
httputil.MakeInternalAPI("PerformInviteRequest", func(req *http.Request) util.JSONResponse {
var request api.PerformInviteRequest
var response api.PerformInviteResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error())
}
if err := intAPI.PerformInvite(req.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
internalAPIMux.Handle(
FederationAPIPerformDirectoryLookupRequestPath,
httputil.MakeInternalAPI("PerformDirectoryLookupRequest", func(req *http.Request) util.JSONResponse {
var request api.PerformDirectoryLookupRequest
var response api.PerformDirectoryLookupResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error())
}
if err := intAPI.PerformDirectoryLookup(req.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
internalAPIMux.Handle(
FederationAPIPerformBroadcastEDUPath,
httputil.MakeInternalAPI("PerformBroadcastEDU", func(req *http.Request) util.JSONResponse {
var request api.PerformBroadcastEDURequest
var response api.PerformBroadcastEDUResponse
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error())
}
if err := intAPI.PerformBroadcastEDU(req.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
internalAPIMux.Handle(
FederationAPIGetUserDevicesPath,
httputil.MakeInternalAPI("GetUserDevices", func(req *http.Request) util.JSONResponse {

View file

@ -0,0 +1,77 @@
// 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 httputil
import (
"context"
"encoding/json"
"net/http"
"github.com/gorilla/mux"
"github.com/matrix-org/util"
opentracing "github.com/opentracing/opentracing-go"
)
type InternalAPIClient[req, res any] struct {
name string
url string
client *http.Client
}
func NewInternalAPIClient[req, res any](name, url string, httpClient *http.Client) *InternalAPIClient[req, res] {
return &InternalAPIClient[req, res]{
name: name,
url: url,
client: httpClient,
}
}
func (h *InternalAPIClient[req, res]) Call(ctx context.Context, request *req, response *res) error {
span, ctx := opentracing.StartSpanFromContext(ctx, h.name)
defer span.Finish()
return PostJSON(ctx, span, h.client, h.url, request, response)
}
type InternalAPIServer[req, res any] struct {
name string
url string
f func(context.Context, *req, *res) error
}
func NewInternalAPIServer[req, res any](name, url string, f func(context.Context, *req, *res) error) *InternalAPIServer[req, res] {
return &InternalAPIServer[req, res]{
name: name,
url: url,
f: f,
}
}
func (h *InternalAPIServer[req, res]) Serve(mux *mux.Router) {
mux.Handle(
h.url,
MakeInternalAPI(h.name, func(httpReq *http.Request) util.JSONResponse {
var request req
var response res
if err := json.NewDecoder(httpReq.Body).Decode(&request); err != nil {
return util.ErrorResponse(err)
}
if err := h.f(httpReq.Context(), &request, &response); err != nil {
return util.ErrorResponse(err)
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
}