From 7f854224716f1f65ea6e0fabab3e7fe9d51e5491 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 15 Nov 2017 15:42:39 +0000 Subject: [PATCH] Move /state request handling out of RequestPool (#333) We should probably move the handling out from the syncapi, but that requires the clientapi to stream the current state which it currently doesn't. This at least stops the sync and state handling being done in one file. --- .../cmd/dendrite-monolith-server/main.go | 2 +- .../cmd/dendrite-sync-api-server/main.go | 2 +- .../dendrite/syncapi/routing/routing.go | 9 +- .../dendrite/syncapi/routing/state.go | 118 ++++++++++++++++++ .../dendrite/syncapi/sync/requestpool.go | 91 -------------- 5 files changed, 125 insertions(+), 97 deletions(-) create mode 100644 src/github.com/matrix-org/dendrite/syncapi/routing/state.go diff --git a/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go b/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go index 3b597576..ea71fed9 100644 --- a/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go +++ b/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go @@ -330,7 +330,7 @@ func (m *monolith) setupAPIs() { syncapi_routing.Setup(m.api, syncapi_sync.NewRequestPool( m.syncAPIDB, m.syncAPINotifier, m.accountDB, - ), m.deviceDB) + ), m.syncAPIDB, m.deviceDB) federationapi_routing.Setup( m.api, *m.cfg, m.queryAPI, m.roomServerProducer, m.keyRing, m.federation, diff --git a/src/github.com/matrix-org/dendrite/cmd/dendrite-sync-api-server/main.go b/src/github.com/matrix-org/dendrite/cmd/dendrite-sync-api-server/main.go index fb81b0e3..e7f83a60 100644 --- a/src/github.com/matrix-org/dendrite/cmd/dendrite-sync-api-server/main.go +++ b/src/github.com/matrix-org/dendrite/cmd/dendrite-sync-api-server/main.go @@ -104,7 +104,7 @@ func main() { log.Info("Starting sync server on ", cfg.Listen.SyncAPI) api := mux.NewRouter() - routing.Setup(api, sync.NewRequestPool(db, n, adb), deviceDB) + routing.Setup(api, sync.NewRequestPool(db, n, adb), db, deviceDB) common.SetupHTTPAPI(http.DefaultServeMux, api) log.Fatal(http.ListenAndServe(string(cfg.Listen.SyncAPI), nil)) diff --git a/src/github.com/matrix-org/dendrite/syncapi/routing/routing.go b/src/github.com/matrix-org/dendrite/syncapi/routing/routing.go index 15ca267a..6cdbe992 100644 --- a/src/github.com/matrix-org/dendrite/syncapi/routing/routing.go +++ b/src/github.com/matrix-org/dendrite/syncapi/routing/routing.go @@ -21,6 +21,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/storage/devices" "github.com/matrix-org/dendrite/common" + "github.com/matrix-org/dendrite/syncapi/storage" "github.com/matrix-org/dendrite/syncapi/sync" "github.com/matrix-org/util" ) @@ -28,7 +29,7 @@ import ( const pathPrefixR0 = "/_matrix/client/r0" // Setup configures the given mux with sync-server listeners -func Setup(apiMux *mux.Router, srp *sync.RequestPool, deviceDB *devices.Database) { +func Setup(apiMux *mux.Router, srp *sync.RequestPool, syncDB *storage.SyncServerDatabase, deviceDB *devices.Database) { r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter() r0mux.Handle("/sync", common.MakeAuthAPI("sync", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { @@ -37,16 +38,16 @@ func Setup(apiMux *mux.Router, srp *sync.RequestPool, deviceDB *devices.Database r0mux.Handle("/rooms/{roomID}/state", common.MakeAuthAPI("room_state", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars := mux.Vars(req) - return srp.OnIncomingStateRequest(req, vars["roomID"]) + return OnIncomingStateRequest(req, syncDB, vars["roomID"]) })).Methods("GET") r0mux.Handle("/rooms/{roomID}/state/{type}", common.MakeAuthAPI("room_state", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars := mux.Vars(req) - return srp.OnIncomingStateTypeRequest(req, vars["roomID"], vars["type"], "") + return OnIncomingStateTypeRequest(req, syncDB, vars["roomID"], vars["type"], "") })).Methods("GET") r0mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", common.MakeAuthAPI("room_state", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars := mux.Vars(req) - return srp.OnIncomingStateTypeRequest(req, vars["roomID"], vars["type"], vars["stateKey"]) + return OnIncomingStateTypeRequest(req, syncDB, vars["roomID"], vars["type"], vars["stateKey"]) })).Methods("GET") } diff --git a/src/github.com/matrix-org/dendrite/syncapi/routing/state.go b/src/github.com/matrix-org/dendrite/syncapi/routing/state.go new file mode 100644 index 00000000..6c825fce --- /dev/null +++ b/src/github.com/matrix-org/dendrite/syncapi/routing/state.go @@ -0,0 +1,118 @@ +// Copyright 2017 Vector Creations Ltd +// +// 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/httputil" + "github.com/matrix-org/dendrite/clientapi/jsonerror" + "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/syncapi/types" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" + log "github.com/sirupsen/logrus" +) + +type stateEventInStateResp struct { + gomatrixserverlib.ClientEvent + PrevContent json.RawMessage `json:"prev_content,omitempty"` + ReplacesState string `json:"replaces_state,omitempty"` +} + +// OnIncomingStateRequest is called when a client makes a /rooms/{roomID}/state +// request. It will fetch all the state events from the specified room and will +// append the necessary keys to them if applicable before returning them. +// Returns an error if something went wrong in the process. +// TODO: Check if the user is in the room. If not, check if the room's history +// is publicly visible. Current behaviour is returning an empty array if the +// user cannot see the room's history. +func OnIncomingStateRequest(req *http.Request, db *storage.SyncServerDatabase, roomID string) util.JSONResponse { + // TODO(#287): Auth request and handle the case where the user has left (where + // we should return the state at the poin they left) + + stateEvents, err := db.GetStateEventsForRoom(req.Context(), roomID) + if err != nil { + return httputil.LogThenError(req, err) + } + + resp := []stateEventInStateResp{} + // Fill the prev_content and replaces_state keys if necessary + for _, event := range stateEvents { + stateEvent := stateEventInStateResp{ + ClientEvent: gomatrixserverlib.ToClientEvent(event, gomatrixserverlib.FormatAll), + } + var prevEventRef types.PrevEventRef + if len(event.Unsigned()) > 0 { + if err := json.Unmarshal(event.Unsigned(), &prevEventRef); err != nil { + return httputil.LogThenError(req, err) + } + // Fills the previous state event ID if the state event replaces another + // state event + if len(prevEventRef.ReplacesState) > 0 { + stateEvent.ReplacesState = prevEventRef.ReplacesState + } + // Fill the previous event if the state event references a previous event + if prevEventRef.PrevContent != nil { + stateEvent.PrevContent = prevEventRef.PrevContent + } + } + + resp = append(resp, stateEvent) + } + + return util.JSONResponse{ + Code: 200, + JSON: resp, + } +} + +// OnIncomingStateTypeRequest is called when a client makes a +// /rooms/{roomID}/state/{type}/{statekey} request. It will look in current +// state to see if there is an event with that type and state key, if there +// is then (by default) we return the content, otherwise a 404. +func OnIncomingStateTypeRequest(req *http.Request, db *storage.SyncServerDatabase, roomID string, evType, stateKey string) util.JSONResponse { + // TODO(#287): Auth request and handle the case where the user has left (where + // we should return the state at the poin they left) + + logger := util.GetLogger(req.Context()) + logger.WithFields(log.Fields{ + "roomID": roomID, + "evType": evType, + "stateKey": stateKey, + }).Info("Fetching state") + + event, err := db.GetStateEvent(req.Context(), roomID, evType, stateKey) + if err != nil { + return httputil.LogThenError(req, err) + } + + if event == nil { + return util.JSONResponse{ + Code: 404, + JSON: jsonerror.NotFound("cannot find state"), + } + } + + stateEvent := stateEventInStateResp{ + ClientEvent: gomatrixserverlib.ToClientEvent(*event, gomatrixserverlib.FormatAll), + } + + return util.JSONResponse{ + Code: 200, + JSON: stateEvent.Content, + } +} diff --git a/src/github.com/matrix-org/dendrite/syncapi/sync/requestpool.go b/src/github.com/matrix-org/dendrite/syncapi/sync/requestpool.go index ebfb140d..3b677561 100644 --- a/src/github.com/matrix-org/dendrite/syncapi/sync/requestpool.go +++ b/src/github.com/matrix-org/dendrite/syncapi/sync/requestpool.go @@ -15,7 +15,6 @@ package sync import ( - "encoding/json" "net/http" "time" @@ -120,96 +119,6 @@ func (rp *RequestPool) OnIncomingSyncRequest(req *http.Request, device *authtype } } -type stateEventInStateResp struct { - gomatrixserverlib.ClientEvent - PrevContent json.RawMessage `json:"prev_content,omitempty"` - ReplacesState string `json:"replaces_state,omitempty"` -} - -// OnIncomingStateRequest is called when a client makes a /rooms/{roomID}/state -// request. It will fetch all the state events from the specified room and will -// append the necessary keys to them if applicable before returning them. -// Returns an error if something went wrong in the process. -// TODO: Check if the user is in the room. If not, check if the room's history -// is publicly visible. Current behaviour is returning an empty array if the -// user cannot see the room's history. -func (rp *RequestPool) OnIncomingStateRequest(req *http.Request, roomID string) util.JSONResponse { - // TODO(#287): Auth request and handle the case where the user has left (where - // we should return the state at the poin they left) - - stateEvents, err := rp.db.GetStateEventsForRoom(req.Context(), roomID) - if err != nil { - return httputil.LogThenError(req, err) - } - - resp := []stateEventInStateResp{} - // Fill the prev_content and replaces_state keys if necessary - for _, event := range stateEvents { - stateEvent := stateEventInStateResp{ - ClientEvent: gomatrixserverlib.ToClientEvent(event, gomatrixserverlib.FormatAll), - } - var prevEventRef types.PrevEventRef - if len(event.Unsigned()) > 0 { - if err := json.Unmarshal(event.Unsigned(), &prevEventRef); err != nil { - return httputil.LogThenError(req, err) - } - // Fills the previous state event ID if the state event replaces another - // state event - if len(prevEventRef.ReplacesState) > 0 { - stateEvent.ReplacesState = prevEventRef.ReplacesState - } - // Fill the previous event if the state event references a previous event - if prevEventRef.PrevContent != nil { - stateEvent.PrevContent = prevEventRef.PrevContent - } - } - - resp = append(resp, stateEvent) - } - - return util.JSONResponse{ - Code: 200, - JSON: resp, - } -} - -// OnIncomingStateTypeRequest is called when a client makes a -// /rooms/{roomID}/state/{type}/{statekey} request. It will look in current -// state to see if there is an event with that type and state key, if there -// is then (by default) we return the content, otherwise a 404. -func (rp *RequestPool) OnIncomingStateTypeRequest(req *http.Request, roomID string, evType, stateKey string) util.JSONResponse { - // TODO(#287): Auth request and handle the case where the user has left (where - // we should return the state at the poin they left) - - logger := util.GetLogger(req.Context()) - logger.WithFields(log.Fields{ - "roomID": roomID, - "evType": evType, - "stateKey": stateKey, - }).Info("Fetching state") - - event, err := rp.db.GetStateEvent(req.Context(), roomID, evType, stateKey) - if err != nil { - return httputil.LogThenError(req, err) - } - - if event == nil { - return util.JSONResponse{ - Code: 404, - JSON: jsonerror.NotFound("cannot find state"), - } - } - - stateEvent := stateEventInStateResp{ - ClientEvent: gomatrixserverlib.ToClientEvent(*event, gomatrixserverlib.FormatAll), - } - - return util.JSONResponse{ - Code: 200, - JSON: stateEvent.Content, - } -} - func (rp *RequestPool) currentSyncForUser(req syncRequest, currentPos types.StreamPosition) (res *types.Response, err error) { // TODO: handle ignored users if req.since == types.StreamPosition(0) {