Add structs for HTTP long-polling (#56)
This commit is contained in:
parent
c1c837516a
commit
de9e3e5417
|
@ -1,20 +0,0 @@
|
||||||
package readers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
|
||||||
"github.com/matrix-org/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sync implements /sync
|
|
||||||
func Sync(req *http.Request) util.JSONResponse {
|
|
||||||
logger := util.GetLogger(req.Context())
|
|
||||||
userID, resErr := auth.VerifyAccessToken(req)
|
|
||||||
if resErr != nil {
|
|
||||||
return *resErr
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.WithField("userID", userID).Info("Doing stuff...")
|
|
||||||
return util.MessageResponse(404, "Not implemented yet")
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/clientapi/config"
|
"github.com/matrix-org/dendrite/clientapi/config"
|
||||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||||
"github.com/matrix-org/dendrite/clientapi/readers"
|
"github.com/matrix-org/dendrite/clientapi/sync"
|
||||||
"github.com/matrix-org/dendrite/clientapi/writers"
|
"github.com/matrix-org/dendrite/clientapi/writers"
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
|
@ -49,11 +49,11 @@ func Setup(servMux *http.ServeMux, httpClient *http.Client, cfg config.ClientAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupSyncServerListeners configures the given mux with sync-server listeners
|
// SetupSyncServerListeners configures the given mux with sync-server listeners
|
||||||
func SetupSyncServerListeners(servMux *http.ServeMux, httpClient *http.Client, cfg config.Sync) {
|
func SetupSyncServerListeners(servMux *http.ServeMux, httpClient *http.Client, cfg config.Sync, srp sync.RequestPool) {
|
||||||
apiMux := mux.NewRouter()
|
apiMux := mux.NewRouter()
|
||||||
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
|
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
|
||||||
r0mux.Handle("/sync", make("sync", util.NewJSONRequestHandler(func(req *http.Request) util.JSONResponse {
|
r0mux.Handle("/sync", make("sync", util.NewJSONRequestHandler(func(req *http.Request) util.JSONResponse {
|
||||||
return readers.Sync(req)
|
return srp.OnIncomingSyncRequest(req)
|
||||||
})))
|
})))
|
||||||
servMux.Handle("/metrics", prometheus.Handler())
|
servMux.Handle("/metrics", prometheus.Handler())
|
||||||
servMux.Handle("/api/", http.StripPrefix("/api", apiMux))
|
servMux.Handle("/api/", http.StripPrefix("/api", apiMux))
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (s *outputRoomEventsStatements) Events(txn *sql.Tx, eventIDs []string) ([]g
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
result := make([]gomatrixserverlib.Event, len(eventIDs))
|
var result []gomatrixserverlib.Event
|
||||||
i := 0
|
i := 0
|
||||||
for ; rows.Next(); i++ {
|
for ; rows.Next(); i++ {
|
||||||
var eventBytes []byte
|
var eventBytes []byte
|
||||||
|
@ -86,7 +86,7 @@ func (s *outputRoomEventsStatements) Events(txn *sql.Tx, eventIDs []string) ([]g
|
||||||
result = append(result, ev)
|
result = append(result, ev)
|
||||||
}
|
}
|
||||||
if i != len(eventIDs) {
|
if i != len(eventIDs) {
|
||||||
return nil, fmt.Errorf("failed to map all event IDs to events: (%d != %d)", i, len(eventIDs))
|
return nil, fmt.Errorf("failed to map all event IDs to events: (got %d, wanted %d)", i, len(eventIDs))
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
101
src/github.com/matrix-org/dendrite/clientapi/sync/requestpool.go
Normal file
101
src/github.com/matrix-org/dendrite/clientapi/sync/requestpool.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/storage"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultSyncTimeout = time.Duration(30) * time.Second
|
||||||
|
|
||||||
|
type syncRequest struct {
|
||||||
|
userID string
|
||||||
|
timeout time.Duration
|
||||||
|
since string
|
||||||
|
wantFullState bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestPool manages HTTP long-poll connections for /sync
|
||||||
|
type RequestPool struct {
|
||||||
|
db *storage.SyncServerDatabase
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnIncomingSyncRequest is called when a client makes a /sync request. This function MUST be
|
||||||
|
// called in a dedicated goroutine for this request. This function will block the goroutine
|
||||||
|
// until a response is ready, or it times out.
|
||||||
|
func (rp *RequestPool) OnIncomingSyncRequest(req *http.Request) util.JSONResponse {
|
||||||
|
// Extract values from request
|
||||||
|
logger := util.GetLogger(req.Context())
|
||||||
|
userID, resErr := auth.VerifyAccessToken(req)
|
||||||
|
if resErr != nil {
|
||||||
|
return *resErr
|
||||||
|
}
|
||||||
|
since := req.URL.Query().Get("since")
|
||||||
|
timeout := getTimeout(req.URL.Query().Get("timeout"))
|
||||||
|
fullState := req.URL.Query().Get("full_state")
|
||||||
|
wantFullState := fullState != "" && fullState != "false"
|
||||||
|
// TODO: Additional query params: set_presence, filter
|
||||||
|
syncReq := syncRequest{
|
||||||
|
userID: userID,
|
||||||
|
timeout: timeout,
|
||||||
|
since: since,
|
||||||
|
wantFullState: wantFullState,
|
||||||
|
}
|
||||||
|
logger.WithFields(log.Fields{
|
||||||
|
"userID": userID,
|
||||||
|
"since": since,
|
||||||
|
"timeout": timeout,
|
||||||
|
}).Info("Incoming /sync request")
|
||||||
|
|
||||||
|
res, err := rp.currentSyncForUser(syncReq)
|
||||||
|
if err != nil {
|
||||||
|
return httputil.LogThenError(req, err)
|
||||||
|
}
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: 200,
|
||||||
|
JSON: res,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnNewEvent is called when a new event is received from the room server
|
||||||
|
func (rp *RequestPool) OnNewEvent(ev *gomatrixserverlib.Event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rp *RequestPool) currentSyncForUser(req syncRequest) ([]gomatrixserverlib.Event, error) {
|
||||||
|
// https://github.com/matrix-org/synapse/blob/v0.19.3/synapse/handlers/sync.py#L179
|
||||||
|
// Check if we are going to return immediately and if so, calculate the current
|
||||||
|
// sync for this user and return.
|
||||||
|
if req.since == "" || req.timeout == time.Duration(0) || req.wantFullState {
|
||||||
|
return []gomatrixserverlib.Event{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: wait for an event which affects this user or one of their rooms, then recheck for new
|
||||||
|
// sync data.
|
||||||
|
time.Sleep(req.timeout)
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTimeout(timeoutMS string) time.Duration {
|
||||||
|
if timeoutMS == "" {
|
||||||
|
return defaultSyncTimeout
|
||||||
|
}
|
||||||
|
i, err := strconv.Atoi(timeoutMS)
|
||||||
|
if err != nil {
|
||||||
|
return defaultSyncTimeout
|
||||||
|
}
|
||||||
|
return time.Duration(i) * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequestPool makes a new RequestPool
|
||||||
|
func NewRequestPool(db *storage.SyncServerDatabase) RequestPool {
|
||||||
|
return RequestPool{db}
|
||||||
|
}
|
|
@ -16,10 +16,11 @@ import (
|
||||||
type Server struct {
|
type Server struct {
|
||||||
roomServerConsumer *common.ContinualConsumer
|
roomServerConsumer *common.ContinualConsumer
|
||||||
db *storage.SyncServerDatabase
|
db *storage.SyncServerDatabase
|
||||||
|
rp RequestPool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new sync server. Call Start() to begin consuming from room servers.
|
// NewServer creates a new sync server. Call Start() to begin consuming from room servers.
|
||||||
func NewServer(cfg *config.Sync, store *storage.SyncServerDatabase) (*Server, error) {
|
func NewServer(cfg *config.Sync, rp RequestPool, store *storage.SyncServerDatabase) (*Server, error) {
|
||||||
kafkaConsumer, err := sarama.NewConsumer(cfg.KafkaConsumerURIs, nil)
|
kafkaConsumer, err := sarama.NewConsumer(cfg.KafkaConsumerURIs, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -33,6 +34,7 @@ func NewServer(cfg *config.Sync, store *storage.SyncServerDatabase) (*Server, er
|
||||||
s := &Server{
|
s := &Server{
|
||||||
roomServerConsumer: &consumer,
|
roomServerConsumer: &consumer,
|
||||||
db: store,
|
db: store,
|
||||||
|
rp: rp,
|
||||||
}
|
}
|
||||||
consumer.ProcessMessage = s.onMessage
|
consumer.ProcessMessage = s.onMessage
|
||||||
|
|
||||||
|
@ -73,6 +75,7 @@ func (s *Server) onMessage(msg *sarama.ConsumerMessage) error {
|
||||||
}).Panicf("roomserver output log: write event failure")
|
}).Panicf("roomserver output log: write event failure")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
s.rp.OnNewEvent(&ev)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,9 @@ func main() {
|
||||||
log.Panicf("startup: failed to create sync server database with data source %s : %s", cfg.DataSource, err)
|
log.Panicf("startup: failed to create sync server database with data source %s : %s", cfg.DataSource, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
server, err := sync.NewServer(cfg, db)
|
rp := sync.NewRequestPool(db)
|
||||||
|
|
||||||
|
server, err := sync.NewServer(cfg, rp, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("startup: failed to create sync server: %s", err)
|
log.Panicf("startup: failed to create sync server: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -83,6 +85,6 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Starting sync server on ", *bindAddr)
|
log.Info("Starting sync server on ", *bindAddr)
|
||||||
routing.SetupSyncServerListeners(http.DefaultServeMux, http.DefaultClient, *cfg)
|
routing.SetupSyncServerListeners(http.DefaultServeMux, http.DefaultClient, *cfg, rp)
|
||||||
log.Fatal(http.ListenAndServe(*bindAddr, nil))
|
log.Fatal(http.ListenAndServe(*bindAddr, nil))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue