mirror of
https://github.com/matrix-org/sliding-sync.git
synced 2025-03-10 13:37:11 +00:00
Flesh out client, fix session management
- Request first 100 rooms, with placeholder icons - Add a `http.FileServer` for everything under `/client` for images. - Sessions are only 400d if they provide a `?pos=` with the session ID.
This commit is contained in:
parent
3f9794ef33
commit
415575d942
@ -2,12 +2,36 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Sync v3 experiments</title>
|
<title>Sync v3 experiments</title>
|
||||||
<script>
|
<script>
|
||||||
let restart = false;
|
let activeSessionId;
|
||||||
const doSyncLoop = async(accessToken) => {
|
const roomIdToRoom = {};
|
||||||
|
const roomIdToDiv = {}; // cached innerHTML
|
||||||
|
const render = (container, rooms) => {
|
||||||
|
// TODO: don't nuke all cells
|
||||||
|
while (container.firstChild) {
|
||||||
|
container.removeChild(container.firstChild);
|
||||||
|
}
|
||||||
|
rooms.forEach((r) => {
|
||||||
|
roomIdToRoom[r.room_id] = r;
|
||||||
|
const template = document.getElementById("roomCellTemplate");
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template#avoiding_documentfragment_pitfall
|
||||||
|
const roomCell = template.content.firstElementChild.cloneNode(true);
|
||||||
|
roomCell.addEventListener("click", (e) => {
|
||||||
|
console.log(r, e);
|
||||||
|
});
|
||||||
|
roomIdToDiv[r.room_id] = roomCell;
|
||||||
|
roomCell.getElementsByClassName("roomName")[0].textContent = r.name;
|
||||||
|
container.appendChild(roomCell);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const doSyncLoop = async(accessToken, sessionId) => {
|
||||||
|
console.log("Starting sync loop. Active: ", activeSessionId, " this:", sessionId);
|
||||||
let currentPos;
|
let currentPos;
|
||||||
let rooms = [];
|
let rooms = [];
|
||||||
while (!restart) {
|
while (sessionId === activeSessionId) {
|
||||||
let resp = await doSyncRequest(accessToken, currentPos, [0,9]);
|
let resp = await doSyncRequest(accessToken, currentPos, [0,99], sessionId);
|
||||||
currentPos = resp.pos;
|
currentPos = resp.pos;
|
||||||
if (!resp.ops) {
|
if (!resp.ops) {
|
||||||
continue;
|
continue;
|
||||||
@ -60,28 +84,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
render(document.getElementById("listContainer"), rooms);
|
||||||
let output = "";
|
|
||||||
rooms.forEach((r) => {
|
|
||||||
if (!r) {
|
|
||||||
output += "_____\r\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
output += (r.name || r.room_id) + "\r\n";
|
|
||||||
})
|
|
||||||
|
|
||||||
// re-render the rooms array
|
|
||||||
console.log(rooms);
|
|
||||||
console.log(output);
|
|
||||||
document.getElementById("list").textContent = output;
|
|
||||||
}
|
}
|
||||||
console.log("restarting");
|
console.log("active session: ", activeSessionId, " this session: ", sessionId, " terminating.");
|
||||||
document.getElementById("list").textContent = "";
|
|
||||||
restart = false;
|
|
||||||
doSyncLoop(accessToken);
|
|
||||||
}
|
}
|
||||||
// accessToken = string, pos = int, ranges = [2]int e.g [0,99]
|
// accessToken = string, pos = int, ranges = [2]int e.g [0,99]
|
||||||
const doSyncRequest = async (accessToken, pos, ranges) => {
|
const doSyncRequest = async (accessToken, pos, ranges, sessionId) => {
|
||||||
let resp = await fetch("/_matrix/client/v3/sync" + (pos ? "?pos=" + pos : ""), {
|
let resp = await fetch("/_matrix/client/v3/sync" + (pos ? "?pos=" + pos : ""), {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@ -89,7 +97,8 @@
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
rooms: [ranges]
|
rooms: [ranges],
|
||||||
|
session_id: (sessionId ? sessionId : undefined),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let respBody = await resp.json();
|
let respBody = await resp.json();
|
||||||
@ -98,12 +107,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('load', (event) => {
|
window.addEventListener('load', (event) => {
|
||||||
|
const storedAccessToken = window.localStorage.getItem("accessToken");
|
||||||
|
if (storedAccessToken) {
|
||||||
|
document.getElementById("accessToken").value = storedAccessToken;
|
||||||
|
}
|
||||||
document.getElementById("syncButton").onclick = () => {
|
document.getElementById("syncButton").onclick = () => {
|
||||||
const accessToken = document.getElementById("accessToken").value;
|
const accessToken = document.getElementById("accessToken").value;
|
||||||
doSyncLoop(accessToken);
|
window.localStorage.setItem("accessToken", accessToken);
|
||||||
|
doSyncLoop(accessToken, activeSessionId);
|
||||||
}
|
}
|
||||||
document.getElementById("resetButton").onclick = () => {
|
document.getElementById("resetButton").onclick = () => {
|
||||||
restart = true;
|
activeSessionId = new Date().getTime() + "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -112,8 +126,14 @@
|
|||||||
<div>
|
<div>
|
||||||
<input id="accessToken" type="password" placeholder="matrix.org access token" />
|
<input id="accessToken" type="password" placeholder="matrix.org access token" />
|
||||||
<input id="syncButton" type="button" value="Sync" />
|
<input id="syncButton" type="button" value="Sync" />
|
||||||
<input id="resetButton" type="button" value="Reset" />
|
<input id="resetButton" type="button" value="New Session" />
|
||||||
<p id="list" style="white-space: pre;"></p>
|
<div id="listContainer" ></div>
|
||||||
|
<template id="roomCellTemplate">
|
||||||
|
<div style="padding: 5px; display: flex; align-items: center;">
|
||||||
|
<img src="/client/placeholder.svg" style="width: 32px; height: 32px;"/>
|
||||||
|
<span class="roomName"> </span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
5
client/placeholder.svg
Normal file
5
client/placeholder.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.0" width="150" height="150">
|
||||||
|
<path fill="#ccc" d="M 104.68731,56.689353 C 102.19435,80.640493 93.104981,97.26875 74.372196,97.26875 55.639402,97.26875 46.988823,82.308034 44.057005,57.289941 41.623314,34.938838 55.639402,15.800152 74.372196,15.800152 c 18.732785,0 32.451944,18.493971 30.315114,40.889201 z"/>
|
||||||
|
<path fill="#ccc" d="M 92.5675 89.6048 C 90.79484 93.47893 89.39893 102.4504 94.86478 106.9039 C 103.9375 114.2963 106.7064 116.4723 118.3117 118.9462 C 144.0432 124.4314 141.6492 138.1543 146.5244 149.2206 L 4.268444 149.1023 C 8.472223 138.6518 6.505799 124.7812 32.40051 118.387 C 41.80992 116.0635 45.66513 113.8823 53.58659 107.0158 C 58.52744 102.7329 57.52583 93.99267 56.43084 89.26926 C 52.49275 88.83011 94.1739 88.14054 92.5675 89.6048 z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 918 B |
@ -86,8 +86,11 @@ func (h *SyncLiveHandler) serve(w http.ResponseWriter, req *http.Request) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if requestBody.SessionID == "" {
|
||||||
|
requestBody.SessionID = DefaultSessionID
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := h.setupConnection(req, &requestBody)
|
conn, err := h.setupConnection(req, &requestBody, req.URL.Query().Get("pos") != "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hlog.FromRequest(req).Err(err).Msg("failed to get or create Conn")
|
hlog.FromRequest(req).Err(err).Msg("failed to get or create Conn")
|
||||||
return err
|
return err
|
||||||
@ -127,7 +130,7 @@ func (h *SyncLiveHandler) serve(w http.ResponseWriter, req *http.Request) error
|
|||||||
// setupConnection associates this request with an existing connection or makes a new connection.
|
// setupConnection associates this request with an existing connection or makes a new connection.
|
||||||
// It also sets a v2 sync poll loop going if one didn't exist already for this user.
|
// It also sets a v2 sync poll loop going if one didn't exist already for this user.
|
||||||
// When this function returns, the connection is alive and active.
|
// When this function returns, the connection is alive and active.
|
||||||
func (h *SyncLiveHandler) setupConnection(req *http.Request, syncReq *Request) (*Conn, error) {
|
func (h *SyncLiveHandler) setupConnection(req *http.Request, syncReq *Request, containsPos bool) (*Conn, error) {
|
||||||
log := hlog.FromRequest(req)
|
log := hlog.FromRequest(req)
|
||||||
var conn *Conn
|
var conn *Conn
|
||||||
|
|
||||||
@ -142,7 +145,7 @@ func (h *SyncLiveHandler) setupConnection(req *http.Request, syncReq *Request) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// client thinks they have a connection
|
// client thinks they have a connection
|
||||||
if syncReq.SessionID != "" {
|
if containsPos {
|
||||||
// Lookup the connection
|
// Lookup the connection
|
||||||
// we need to map based on both as the session ID isn't crypto secure but the device ID is (Auth header)
|
// we need to map based on both as the session ID isn't crypto secure but the device ID is (Auth header)
|
||||||
conn = h.ConnMap.Conn(ConnID{
|
conn = h.ConnMap.Conn(ConnID{
|
||||||
@ -202,7 +205,7 @@ func (h *SyncLiveHandler) setupConnection(req *http.Request, syncReq *Request) (
|
|||||||
// to check for an existing connection though, as it's possible for the client to call /sync
|
// to check for an existing connection though, as it's possible for the client to call /sync
|
||||||
// twice for a new connection and get the same session ID.
|
// twice for a new connection and get the same session ID.
|
||||||
conn, created := h.ConnMap.GetOrCreateConn(ConnID{
|
conn, created := h.ConnMap.GetOrCreateConn(ConnID{
|
||||||
SessionID: DefaultSessionID,
|
SessionID: syncReq.SessionID,
|
||||||
DeviceID: deviceID,
|
DeviceID: deviceID,
|
||||||
}, v2device.UserID)
|
}, v2device.UserID)
|
||||||
if created {
|
if created {
|
||||||
@ -210,7 +213,6 @@ func (h *SyncLiveHandler) setupConnection(req *http.Request, syncReq *Request) (
|
|||||||
} else {
|
} else {
|
||||||
log.Info().Str("conn_id", conn.ConnID.String()).Msg("using existing connection")
|
log.Info().Str("conn_id", conn.ConnID.String()).Msg("using existing connection")
|
||||||
}
|
}
|
||||||
syncReq.SessionID = conn.ConnID.SessionID
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
v3.go
26
v3.go
@ -3,7 +3,6 @@ package syncv3
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@ -31,7 +30,7 @@ func (s *server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
h.ServeHTTP(w, req)
|
h.ServeHTTP(w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsClient(file []byte) http.HandlerFunc {
|
func allowCORS(next http.Handler) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, req *http.Request) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
if req.Method == "OPTIONS" {
|
if req.Method == "OPTIONS" {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
@ -40,29 +39,20 @@ func jsClient(file []byte) http.HandlerFunc {
|
|||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: remove when don't need live updates
|
next.ServeHTTP(w, req)
|
||||||
jsFile, err := ioutil.ReadFile("client.html")
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal().Err(err).Msg("failed to read client.html")
|
|
||||||
}
|
|
||||||
w.WriteHeader(200)
|
|
||||||
w.Write(jsFile)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunSyncV3Server is the main entry point to the server
|
// RunSyncV3Server is the main entry point to the server
|
||||||
func RunSyncV3Server(h http.Handler, bindAddr string) {
|
func RunSyncV3Server(h http.Handler, bindAddr string) {
|
||||||
|
|
||||||
jsFile, err := ioutil.ReadFile("client.html")
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal().Err(err).Msg("failed to read client.html")
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP path routing
|
// HTTP path routing
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.Handle("/_matrix/client/v3/sync", h)
|
r.Handle("/_matrix/client/v3/sync", h)
|
||||||
r.HandleFunc("/client", jsClient(jsFile)).Methods("GET", "OPTIONS", "HEAD")
|
r.PathPrefix("/client/").HandlerFunc(
|
||||||
|
allowCORS(
|
||||||
|
http.StripPrefix("/client/", http.FileServer(http.Dir("./client"))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
srv := &server{
|
srv := &server{
|
||||||
chain: []func(next http.Handler) http.Handler{
|
chain: []func(next http.Handler) http.Handler{
|
||||||
@ -73,7 +63,7 @@ func RunSyncV3Server(h http.Handler, bindAddr string) {
|
|||||||
Int("status", status).
|
Int("status", status).
|
||||||
Int("size", size).
|
Int("size", size).
|
||||||
Dur("duration", duration).
|
Dur("duration", duration).
|
||||||
Str("since", r.URL.Query().Get("since")).
|
Str("path", r.URL.Path).
|
||||||
Msg("")
|
Msg("")
|
||||||
}),
|
}),
|
||||||
hlog.RemoteAddrHandler("ip"),
|
hlog.RemoteAddrHandler("ip"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user