From 42b9cbcce7072feb2c7bd9fc9c061404a3b41ac7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 5 Aug 2022 15:29:08 +0100 Subject: [PATCH] Generic-based internal HTTP API (tested out on a few endpoints in the federation API) --- federationapi/inthttp/client.go | 38 +++++++------ federationapi/inthttp/server.go | 93 +++++++++----------------------- internal/httputil/internalapi.go | 77 ++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 85 deletions(-) create mode 100644 internal/httputil/internalapi.go diff --git a/federationapi/inthttp/client.go b/federationapi/inthttp/client.go index 295ddc495..2e207eb06 100644 --- a/federationapi/inthttp/client.go +++ b/federationapi/inthttp/client.go @@ -48,13 +48,31 @@ func NewFederationAPIClient(federationSenderURL string, httpClient *http.Client, if httpClient == nil { return nil, errors.New("NewFederationInternalAPIHTTP: httpClient is ") } - 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 diff --git a/federationapi/inthttp/server.go b/federationapi/inthttp/server.go index 28e52b32d..04d69c5aa 100644 --- a/federationapi/inthttp/server.go +++ b/federationapi/inthttp/server.go @@ -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 { diff --git a/internal/httputil/internalapi.go b/internal/httputil/internalapi.go new file mode 100644 index 000000000..a84378d7a --- /dev/null +++ b/internal/httputil/internalapi.go @@ -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} + }), + ) +}