on master help sign in
Fix 8 server.knot bugs: message expiry boundaries, empty field validation, federation ack reliability, session caps, cursor pagination, and gossip tie-breaking
0ed7a46iko
server.knot+54−17
@@ -107,17 +107,25 @@ forwardToAll = \servers path headers body ->
107 then yield False {}
107 then yield False {}
108 else do
108 else do
109 let server = fold (\acc s -> if acc == "" then s else acc) "" servers
109 let server = fold (\acc s -> if acc == "" then s else acc) "" servers
110+ let rest = do s <- servers; where not (s == server); yield s
111+ if server == ""
112+ then yield False {}
113+ else do
110− result <- httpPost (federationScheme ++ server ++ path) headers body
114+ result <- httpPost (federationScheme ++ server ++ path) headers body
111− restOk <- forwardToAll (do s <- servers; where not (s == server); yield s) path headers body
115+ restOk <- forwardToAll rest path headers body
112− yield (if result.ok then True {} else restOk)
116+ yield (if result.ok then True {} else restOk)
113
117
114sendToEach = \servers action ->
118sendToEach = \servers action ->
115 if count servers == 0
119 if count servers == 0
116 then yield {}
120 then yield {}
117 else do
121 else do
118 let server = fold (\acc s -> if acc == "" then s else acc) "" servers
122 let server = fold (\acc s -> if acc == "" then s else acc) "" servers
119− action server
120− sendToEach (do s <- servers; where not (s == server); yield s) action
123+ let rest = do s <- servers; where not (s == server); yield s
124+ if server == ""
125+ then yield {}
126+ else do
127+ action server
128+ sendToEach rest action
121
129
122pruneExpired = \t -> do
130pruneExpired = \t -> do
123 challenges <- *challenges
131 challenges <- *challenges
@@ -138,7 +146,7 @@ pruneExpired = \t -> do
138 messages <- *messages
146 messages <- *messages
139 set *messages = do
147 set *messages = do
140 m <- messages
148 m <- messages
141− where m.timestamp > t - messageMaxAge
149+ where m.timestamp >= t - messageMaxAge
142 yield m
150 yield m
143
151
144-- ============================================
152-- ============================================
@@ -181,8 +189,20 @@ serve = \req -> case req of
181 c <- challenges
189 c <- challenges
182 where not (c.pubkey == pubkey && c.challenge == challenge)
190 where not (c.pubkey == pubkey && c.challenge == challenge)
183 yield c
191 yield c
192+ let existingForKey = do
193+ s <- sessions
194+ where s.pubkey == pubkey
195+ where s.expiresAt > t
196+ yield s
197+ let oldestExpiry = fold (\acc s -> if s.expiresAt < acc then s.expiresAt else acc) expiry existingForKey
198+ let sessionsCapped = if count existingForKey >= 10
199+ then do
200+ s <- sessions
201+ where not (s.pubkey == pubkey && s.expiresAt == oldestExpiry)
202+ yield s
203+ else sessions
184 set *sessions = if sigValid
204 set *sessions = if sigValid
185− then union sessions [{token: token, pubkey: pubkey, expiresAt: expiry}]
205+ then union sessionsCapped [{token: token, pubkey: pubkey, expiresAt: expiry}]
186 else sessions
206 else sessions
187 set *presence = if sigValid
207 set *presence = if sigValid
188 then union
208 then union
@@ -223,21 +243,21 @@ serve = \req -> case req of
223 Just {value: s} -> do
243 Just {value: s} -> do
224 m <- messages
244 m <- messages
225 where m.toKey == s.pubkey
245 where m.toKey == s.pubkey
226− where m.timestamp > t - messageMaxAge
246+ where m.timestamp >= t - messageMaxAge
227 yield {id: m.messageId, eventType: "message",
247 yield {id: m.messageId, eventType: "message",
228 payload: toJson {toKey: m.toKey, fromKey: m.fromKey,
248 payload: toJson {toKey: m.toKey, fromKey: m.fromKey,
229 encryptedBlob: m.encryptedBlob,
249 encryptedBlob: m.encryptedBlob,
230 timestamp: m.timestamp}}
250 timestamp: m.timestamp}}
231 let cursor = case session of
251 let cursor = case session of
232 Nothing {} -> "unauthorized"
252 Nothing {} -> "unauthorized"
233− _ -> ""
253+ _ -> show t
234 yield {events: evts, cursor: cursor}
254 yield {events: evts, cursor: cursor}
235 respond result
255 respond result
236
256
237 SendMessage {to, encryptedBlob, timestamp, authorization, respond} -> do
257 SendMessage {to, encryptedBlob, timestamp, authorization, respond} -> do
238 t <- now
258 t <- now
239 msgId <- genToken
259 msgId <- genToken
240− let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
260+ let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
241 result <- atomic do
261 result <- atomic do
242 pruneExpired t
262 pruneExpired t
243 sessions <- *sessions
263 sessions <- *sessions
@@ -299,9 +319,13 @@ serve = \req -> case req of
299 where m.messageId == mid
319 where m.messageId == mid
300 where m.toKey == s.pubkey
320 where m.toKey == s.pubkey
301 yield m
321 yield m
322+ let localAcked = do
323+ m <- ackedMessages
324+ where m.originServer == ""
325+ yield m
302 set *messages = case session of
326 set *messages = case session of
303 Nothing {} -> messages
327 Nothing {} -> messages
304− Just {value: _} -> diff messages ackedMessages
328+ Just {value: _} -> diff messages localAcked
305 let ok = case session of
329 let ok = case session of
306 Nothing {} -> False {}
330 Nothing {} -> False {}
307 _ -> True {}
331 _ -> True {}
@@ -315,11 +339,20 @@ serve = \req -> case req of
315 if count (do a <- acc; where a == m.originServer; yield a) > 0 then acc
339 if count (do a <- acc; where a == m.originServer; yield a) > 0 then acc
316 else union acc [m.originServer]) [] fedMessages
340 else union acc [m.originServer]) [] fedMessages
317 if result.ok && count originServers > 0 && not (federationSecret == "")
341 if result.ok && count originServers > 0 && not (federationSecret == "")
318− then sendToEach originServers (\server ->
342+ then sendToEach originServers (\server -> do
319− httpPost (federationScheme ++ server ++ "/federation/ack")
343+ ackResult <- httpPost (federationScheme ++ server ++ "/federation/ack")
320 {authorization: "Bearer " ++ federationSecret}
344 {authorization: "Bearer " ++ federationSecret}
321 {messageIds: (do m <- fedMessages; where m.originServer == server; yield m.messageId),
345 {messageIds: (do m <- fedMessages; where m.originServer == server; yield m.messageId),
322− originServer: server, fromServer: serverHost})
346+ originServer: server, fromServer: serverHost}
347+ if ackResult.ok
348+ then atomic do
349+ messages <- *messages
350+ set *messages = diff messages (do
351+ m <- messages
352+ mid <- (do fm <- fedMessages; where fm.originServer == server; yield fm.messageId)
353+ where m.messageId == mid
354+ yield m)
355+ else yield {})
323 else yield {}
356 else yield {}
324 respond {ok: result.ok}
357 respond {ok: result.ok}
325
358
@@ -361,6 +394,8 @@ serve = \req -> case req of
361 let onlineEntries = do
394 let onlineEntries = do
362 e <- events
395 e <- events
363 where e.eventType == "online"
396 where e.eventType == "online"
397+ where not (e.server == "")
398+ where not (e.pubkey == "")
364 where not (e.server == serverHost)
399 where not (e.server == serverHost)
365 where e.timestamp <= t
400 where e.timestamp <= t
366 where e.timestamp + (clampTtl e.ttl) * 1000 > t
401 where e.timestamp + (clampTtl e.ttl) * 1000 > t
@@ -368,6 +403,8 @@ serve = \req -> case req of
368 let offlineEntries = do
403 let offlineEntries = do
369 e <- events
404 e <- events
370 where e.eventType == "offline"
405 where e.eventType == "offline"
406+ where not (e.server == "")
407+ where not (e.pubkey == "")
371 where not (e.server == serverHost)
408 where not (e.server == serverHost)
372 where e.timestamp <= t
409 where e.timestamp <= t
373 where t - e.timestamp < (clampTtl e.ttl) * 1000
410 where t - e.timestamp < (clampTtl e.ttl) * 1000
@@ -378,7 +415,7 @@ serve = \req -> case req of
378 o <- offlineEntries
415 o <- offlineEntries
379 where o.pubkey == e.pubkey
416 where o.pubkey == e.pubkey
380 where o.server == e.server
417 where o.server == e.server
381− where o.timestamp > e.timestamp
418+ where o.timestamp >= e.timestamp
382 yield o) == 0
419 yield o) == 0
383 yield {pubkey: e.pubkey, server: e.server, expiresAt: e.expiresAt}
420 yield {pubkey: e.pubkey, server: e.server, expiresAt: e.expiresAt}
384 let affectedKeys = union
421 let affectedKeys = union
@@ -440,14 +477,14 @@ serve = \req -> case req of
440 t <- now
477 t <- now
441 let authorized = verifyFederationAuth authorization
478 let authorized = verifyFederationAuth authorization
442 let validOrigin = not (originServer == "") && not (originServer == serverHost)
479 let validOrigin = not (originServer == "") && not (originServer == serverHost)
443− let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t - messageMaxAge else timestamp
480+ let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
444 atomic do
481 atomic do
445 pruneExpired t
482 pruneExpired t
446 messages <- *messages
483 messages <- *messages
447 let exists = count (do
484 let exists = count (do
448 m <- messages
485 m <- messages
449 where m.messageId == messageId
486 where m.messageId == messageId
450− where m.timestamp > t - messageMaxAge
487+ where m.timestamp >= t - messageMaxAge
451 yield m) > 0
488 yield m) > 0
452 let shouldInsert = authorized && validOrigin && not exists
489 let shouldInsert = authorized && validOrigin && not exists
453 set *messages = if shouldInsert
490 set *messages = if shouldInsert