on master help sign in
Improve type safety and simplify server: add DeliveryStatus type, eliminate result ADTs
c59feb8iko
client/App.fs+13−25
@@ -44,12 +44,6 @@ module App =
44
44
45 type IncomingEvent = { Sender: string; Timestamp: DateTimeOffset; Envelope: Envelope }
45 type IncomingEvent = { Sender: string; Timestamp: DateTimeOffset; Envelope: Envelope }
46
46
47− type PollStatus =
48− | Idle
49− | Polling
50− | Received of string
51− | PollError of string
52−
53 // ── Model ──
47 // ── Model ──
54
48
55 type Model =
49 type Model =
@@ -57,7 +51,7 @@ module App =
57 Auth: AuthState
51 Auth: AuthState
58 ServerUrl: string
52 ServerUrl: string
59 Contacts: Map<string, Contact>
53 Contacts: Map<string, Contact>
60− PollStatus: PollStatus
54+ PollStatus: string
61 Messages: Map<string, ChatMessage list>
55 Messages: Map<string, ChatMessage list>
62 Error: string option
56 Error: string option
63 PollCursor: int64
57 PollCursor: int64
@@ -80,7 +74,7 @@ module App =
80 | DoSend
74 | DoSend
81 | SendFailed of string
75 | SendFailed of string
82 | DoSaveContact
76 | DoSaveContact
83− | PollResult of events: IncomingEvent list * ackIds: string list * status: PollStatus * cursor: int64
77+ | PollResult of events: IncomingEvent list * ackIds: string list * status: string * cursor: int64
84 | CopyPubKey
78 | CopyPubKey
85 | DismissError
79 | DismissError
86 | StateLoaded of StateLoadResult
80 | StateLoaded of StateLoadResult
@@ -121,11 +115,10 @@ module App =
121 |> Option.filter (fun bytes -> bytes.Length = 32)
115 |> Option.filter (fun bytes -> bytes.Length = 32)
122
116
123 let private contactName (contacts: Map<string, Contact>) (pk: string) =
117 let private contactName (contacts: Map<string, Contact>) (pk: string) =
124− let fallback = truncKey pk
125 match Map.tryFind pk contacts with
118 match Map.tryFind pk contacts with
126− | Some c when c.Nickname <> fallback -> c.Nickname
119+ | Some c when c.Nickname <> "" -> c.Nickname
127 | Some c when c.DisplayName <> "" -> c.DisplayName
120 | Some c when c.DisplayName <> "" -> c.DisplayName
128− | _ -> fallback
121+ | _ -> truncKey pk
129
122
130 let private messagesFor (pk: string) (messages: Map<string, ChatMessage list>) =
123 let private messagesFor (pk: string) (messages: Map<string, ChatMessage list>) =
131 messages |> Map.tryFind pk |> Option.defaultValue []
124 messages |> Map.tryFind pk |> Option.defaultValue []
@@ -155,7 +148,7 @@ module App =
155 { m with
148 { m with
156 Contacts =
149 Contacts =
157 if Map.containsKey evt.Sender m.Contacts then m.Contacts
150 if Map.containsKey evt.Sender m.Contacts then m.Contacts
158− else Map.add evt.Sender (newContact evt.Sender (truncKey evt.Sender)) m.Contacts
151+ else Map.add evt.Sender (newContact evt.Sender "") m.Contacts
159 Messages = m.Messages |> appendMessage evt.Sender message }
152 Messages = m.Messages |> appendMessage evt.Sender message }
160 | Envelope.DeliveryAck ackIds ->
153 | Envelope.DeliveryAck ackIds ->
161 { m with Messages = m.Messages |> markDelivered evt.Sender ackIds }
154 { m with Messages = m.Messages |> markDelivered evt.Sender ackIds }
@@ -186,7 +179,7 @@ module App =
186 { Page = Setup
179 { Page = Setup
187 Auth = NoIdentity
180 Auth = NoIdentity
188 ServerUrl = "http://localhost:8080"
181 ServerUrl = "http://localhost:8080"
189− PollStatus = Idle
182+ PollStatus = ""
190 Contacts = Map.empty
183 Contacts = Map.empty
191 Messages = Map.empty
184 Messages = Map.empty
192 Error = None
185 Error = None
@@ -217,13 +210,13 @@ module App =
217 match model.Auth with
210 match model.Auth with
218 | Identified(id, Connecting) ->
211 | Identified(id, Connecting) ->
219 let session = { Url = model.ServerUrl; Token = token; Identity = id }
212 let session = { Url = model.ServerUrl; Token = token; Identity = id }
220− { model with Auth = Identified(id, Online token); Page = Conversations; Error = None; PollStatus = Polling },
213+ { model with Auth = Identified(id, Online token); Page = Conversations; Error = None; PollStatus = "polling..." },
221 [ CmdPoll(session, model.PollCursor) ]
214 [ CmdPoll(session, model.PollCursor) ]
222 | _ -> model, []
215 | _ -> model, []
223
216
224 | AuthErr err -> { setConn Offline model with Error = Some err }, []
217 | AuthErr err -> { setConn Offline model with Error = Some err }, []
225
218
226− | DoDisconnect -> model |> setConn Offline, []
219+ | DoDisconnect -> { model |> setConn Offline with PollStatus = "" }, []
227
220
228 | DoSend ->
221 | DoSend ->
229 match model.Page, trySession model with
222 match model.Page, trySession model with
@@ -445,15 +438,15 @@ module App =
445 let status =
438 let status =
446 $"evts:{response.Events.Length} msgs:{chatCount} errs:{errors.Length}"
439 $"evts:{response.Events.Length} msgs:{chatCount} errs:{errors.Length}"
447 + (match List.tryHead errors with Some e -> $" [{e}]" | None -> "")
440 + (match List.tryHead errors with Some e -> $" [{e}]" | None -> "")
448− return PollResult(events, ackIds, Received status, response.Cursor)
441+ return PollResult(events, ackIds, status, response.Cursor)
449 with
442 with
450 | :? TimeoutException ->
443 | :? TimeoutException ->
451 log "poll timeout"
444 log "poll timeout"
452− return PollResult([], [], Polling, cursor)
445+ return PollResult([], [], "polling...", cursor)
453 | ex ->
446 | ex ->
454 log $"poll error: {ex.Message}"
447 log $"poll error: {ex.Message}"
455 do! Async.Sleep 3000
448 do! Async.Sleep 3000
456− return PollResult([], [], PollError ex.Message, cursor)
449+ return PollResult([], [], $"poll error: {ex.Message}", cursor)
457 })
450 })
458
451
459 | CmdAckAndDeliver(session, ackIds, events) ->
452 | CmdAckAndDeliver(session, ackIds, events) ->
@@ -514,12 +507,6 @@ module App =
514
507
515 // ── View ──
508 // ── View ──
516
509
517− let private formatPollStatus = function
518− | Idle -> ""
519− | Polling -> "polling..."
520− | Received s -> s
521− | PollError msg -> $"poll error: {msg}"
522−
523 let private viewErrorBanner (error: string option) =
510 let private viewErrorBanner (error: string option) =
524 VStack(spacing = 4.) {
511 VStack(spacing = 4.) {
525 match error with
512 match error with
@@ -608,7 +595,8 @@ module App =
608 }
595 }
609
596
610 let pubPrefix = match model.Auth with Identified(id, _) -> id.PubKeyHex.[..7] | _ -> "?"
597 let pubPrefix = match model.Auth with Identified(id, _) -> id.PubKeyHex.[..7] | _ -> "?"
598+ let pollInfo = if model.PollStatus <> "" then $" | {model.PollStatus}" else ""
611− Label($"{connStatusLabel model.Auth} [{pubPrefix}] | {formatPollStatus model.PollStatus}")
599+ Label($"{connStatusLabel model.Auth} [{pubPrefix}]{pollInfo}")
612 .font(size = 10.)
600 .font(size = 10.)
613 .textColor(Colors.DimGray)
601 .textColor(Colors.DimGray)
614
602
client/obj/Debug/net10.0-ios/iossimulator-arm64/AppManifest.plist

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/_CompileAppManifest.inputs+2−2
@@ -5,7 +5,7 @@ ApplicationTitle=Skrepka
5ApplicationVersion=1
5ApplicationVersion=1
6AppManifest=Platforms/iOS/Info.plist
6AppManifest=Platforms/iOS/Info.plist
7CompiledAppManifest=obj/Debug/net10.0-ios/iossimulator-arm64/AppManifest.plist
7CompiledAppManifest=obj/Debug/net10.0-ios/iossimulator-arm64/AppManifest.plist
8−DefaultSdkVersion=26.2
8+DefaultSdkVersion=26.4
9FontFilesToRegister=
9FontFilesToRegister=
10GenerateApplicationManifest=true
10GenerateApplicationManifest=true
11IsAppExtension=false
11IsAppExtension=false
@@ -16,7 +16,7 @@ ProjectDir=/Users/iko/Developer/skrepka/client
16ResourcePrefix=Platforms/iOS/Resources
16ResourcePrefix=Platforms/iOS/Resources
17ResourceRules=
17ResourceRules=
18SdkIsSimulator=True
18SdkIsSimulator=True
19−SdkVersion=26.2
19+SdkVersion=26.4
20SupportedOSPlatformVersion=26.2
20SupportedOSPlatformVersion=26.2
21TargetArchitectures=ARM64
21TargetArchitectures=ARM64
22TargetFrameworkMoniker=.NETCoreApp,Version=v10.0,Profile=ios
22TargetFrameworkMoniker=.NETCoreApp,Version=v10.0,Profile=ios
client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/_BundleResourceWithLogicalName.itemsdeleted−17
@@ -1,17 +0,0 @@
1−<?xml version="1.0" encoding="utf-8"?>
2−<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3− <ItemGroup>
4− <_BundleResourceWithLogicalName Include="obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/Assets.car">
5− <Optimize>false</Optimize>
6− <LogicalName>Assets.car</LogicalName>
7− </_BundleResourceWithLogicalName>
8− <_BundleResourceWithLogicalName Include="obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/appicon60x60@2x.png">
9− <Optimize>false</Optimize>
10− <LogicalName>appicon60x60@2x.png</LogicalName>
11− </_BundleResourceWithLogicalName>
12− <_BundleResourceWithLogicalName Include="obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/appicon76x76@2x~ipad.png">
13− <Optimize>false</Optimize>
14− <LogicalName>appicon76x76@2x~ipad.png</LogicalName>
15− </_BundleResourceWithLogicalName>
16− </ItemGroup>
17−</Project>
client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/_PartialAppManifest.itemsdeleted−6
@@ -1,6 +0,0 @@
1−<?xml version="1.0" encoding="utf-8"?>
2−<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3− <ItemGroup>
4− <_PostCompilePartialAppManifest Include="obj/Debug/net10.0-ios/iossimulator-arm64/actool/partial-info.plist" />
5− </ItemGroup>
6−</Project>
client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/asset-manifest.plistdeleted−16
@@ -1,16 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8"?>
2−<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3−<plist version="1.0">
4−<dict>
5− <key>com.apple.actool.compilation-results</key>
6− <dict>
7− <key>output-files</key>
8− <array>
9− <string>/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/Assets.car</string>
10− <string>/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/appicon60x60@2x.png</string>
11− <string>/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/appicon76x76@2x~ipad.png</string>
12− <string>/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/partial-info.plist</string>
13− </array>
14− </dict>
15−</dict>
16−</plist>
client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/Assets.cardeleted

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/appicon60x60@2x.pngdeleted

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/bundle/appicon76x76@2x~ipad.pngdeleted

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/actool/partial-info.plistdeleted−31
@@ -1,31 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8"?>
2−<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3−<plist version="1.0">
4−<dict>
5− <key>CFBundleIcons</key>
6− <dict>
7− <key>CFBundlePrimaryIcon</key>
8− <dict>
9− <key>CFBundleIconFiles</key>
10− <array>
11− <string>appicon60x60</string>
12− </array>
13− <key>CFBundleIconName</key>
14− <string>appicon</string>
15− </dict>
16− </dict>
17− <key>CFBundleIcons~ipad</key>
18− <dict>
19− <key>CFBundlePrimaryIcon</key>
20− <dict>
21− <key>CFBundleIconFiles</key>
22− <array>
23− <string>appicon60x60</string>
24− <string>appicon76x76</string>
25− </array>
26− <key>CFBundleIconName</key>
27− <string>appicon</string>
28− </dict>
29− </dict>
30−</dict>
31−</plist>
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/AppManifest.plist

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/_CompileAppManifest.inputs+2−2
@@ -5,7 +5,7 @@ ApplicationTitle=Skrepka
5ApplicationVersion=1
5ApplicationVersion=1
6AppManifest=Platforms/MacCatalyst/Info.plist
6AppManifest=Platforms/MacCatalyst/Info.plist
7CompiledAppManifest=obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/AppManifest.plist
7CompiledAppManifest=obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/AppManifest.plist
8−DefaultSdkVersion=26.2
8+DefaultSdkVersion=26.4
9FontFilesToRegister=
9FontFilesToRegister=
10GenerateApplicationManifest=true
10GenerateApplicationManifest=true
11IsAppExtension=false
11IsAppExtension=false
@@ -15,7 +15,7 @@ ProjectDir=/Users/iko/Developer/skrepka/client
15ResourcePrefix=Platforms/MacCatalyst/Resources
15ResourcePrefix=Platforms/MacCatalyst/Resources
16ResourceRules=
16ResourceRules=
17SdkIsSimulator=False
17SdkIsSimulator=False
18−SdkVersion=26.2
18+SdkVersion=26.4
19SupportedOSPlatformVersion=26.2
19SupportedOSPlatformVersion=26.2
20TargetArchitectures=ARM64
20TargetArchitectures=ARM64
21TargetFrameworkMoniker=.NETCoreApp,Version=v10.0,Profile=maccatalyst
21TargetFrameworkMoniker=.NETCoreApp,Version=v10.0,Profile=maccatalyst
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/_BundleResourceWithLogicalName.itemsdeleted−13
@@ -1,13 +0,0 @@
1−<?xml version="1.0" encoding="utf-8"?>
2−<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3− <ItemGroup>
4− <_BundleResourceWithLogicalName Include="obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/bundle/Assets.car">
5− <Optimize>false</Optimize>
6− <LogicalName>Assets.car</LogicalName>
7− </_BundleResourceWithLogicalName>
8− <_BundleResourceWithLogicalName Include="obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/bundle/appicon.icns">
9− <Optimize>false</Optimize>
10− <LogicalName>appicon.icns</LogicalName>
11− </_BundleResourceWithLogicalName>
12− </ItemGroup>
13−</Project>
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/_PartialAppManifest.itemsdeleted−6
@@ -1,6 +0,0 @@
1−<?xml version="1.0" encoding="utf-8"?>
2−<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3− <ItemGroup>
4− <_PostCompilePartialAppManifest Include="obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/partial-info.plist" />
5− </ItemGroup>
6−</Project>
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/asset-manifest.plistdeleted−15
@@ -1,15 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8"?>
2−<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3−<plist version="1.0">
4−<dict>
5− <key>com.apple.actool.compilation-results</key>
6− <dict>
7− <key>output-files</key>
8− <array>
9− <string>/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/bundle/Assets.car</string>
10− <string>/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/bundle/appicon.icns</string>
11− <string>/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/partial-info.plist</string>
12− </array>
13− </dict>
14−</dict>
15−</plist>
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/bundle/Assets.cardeleted

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/bundle/appicon.icnsdeleted

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/actool/partial-info.plistdeleted−10
@@ -1,10 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8"?>
2−<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3−<plist version="1.0">
4−<dict>
5− <key>CFBundleIconFile</key>
6− <string>appicon</string>
7− <key>CFBundleIconName</key>
8− <string>appicon</string>
9−</dict>
10−</plist>
server.knot+46−43
@@ -87,6 +87,20 @@ impl ToJSON MessageStatus where
87 MsUnauthorized {} -> "unauthorized"
87 MsUnauthorized {} -> "unauthorized"
88 MsRejected {} -> "rejected")
88 MsRejected {} -> "rejected")
89
89
90+data DeliveryStatus = Delivered {} | Federated {} | Queued {}
91+
92+impl ToJSON DeliveryStatus where
93+ toJson ds = toJson (case ds of
94+ Delivered {} -> "delivered"
95+ Federated {} -> "federated"
96+ Queued {} -> "queued")
97+
98+deliveryToStatus : DeliveryStatus -> MessageStatus
99+deliveryToStatus = \ds -> case ds of
100+ Delivered {} -> MsDelivered {}
101+ Federated {} -> MsFederated {}
102+ Queued {} -> MsQueued {}
103+
90isValidBoundedHex = \maxLen s -> isValidHex s && not (tooLong s maxLen) && s == normalizeHex s
104isValidBoundedHex = \maxLen s -> isValidHex s && not (tooLong s maxLen) && s == normalizeHex s
91type PubkeyHex = Text where isValidBoundedHex maxPubkeyLen
105type PubkeyHex = Text where isValidBoundedHex maxPubkeyLen
92type BlobHex = Text where isValidBoundedHex maxBlobLen
106type BlobHex = Text where isValidBoundedHex maxBlobLen
@@ -108,12 +122,9 @@ type PresenceEntry = {pubkey: PubkeyHex, server: AnyServerName, expiresAt: Times
108type ForwardEntry = {messageId: MessageIdHex, toServer: ServerName, createdAt: Timestamp, toKey: PubkeyHex, fromKey: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp}
122type ForwardEntry = {messageId: MessageIdHex, toServer: ServerName, createdAt: Timestamp, toKey: PubkeyHex, fromKey: PubkeyHex, encryptedBlob: BlobHex, timestamp: Timestamp}
109type AckedEntry = {messageId: MessageIdHex, toKey: PubkeyHex, ackedAt: Timestamp, originServer: ServerName}
123type AckedEntry = {messageId: MessageIdHex, toKey: PubkeyHex, ackedAt: Timestamp, originServer: ServerName}
110data AuthResult = Authorized {pubkey: PubkeyHex} | Unauthorized {}
124data AuthResult = Authorized {pubkey: PubkeyHex} | Unauthorized {}
111−data SendResult = SendUnauthorized {} | SendRejected {} | SendDone {status: MessageStatus, remoteServers: [ServerName], fromKey: PubkeyHex}
125+data SendResult = SendUnauthorized {} | SendRejected {} | SendDone {status: DeliveryStatus, remoteServers: [ServerName], fromKey: PubkeyHex}
112data VerifyResult = VerifyFailed {} | VerifyOk {peerServers: [ServerName]}
126data VerifyResult = VerifyFailed {} | VerifyOk {peerServers: [ServerName]}
113−data AckResult = AckUnauthorized {} | AckDone {pendingAcks: [AckedEntry], evictedEntries: [AckedEntry]}
114−data LookupResult = LookupUnauthorized {} | LookupDone {server: AnyServerName}
115−data ForwardResult = ForwardDuplicate {} | ForwardStored {} | ForwardFull {}
116−data PollResult = PollUnauthorized {} | PollDone {events: [PollEvent], cursor: Timestamp}
127+type PollResponse = {events: [PollEvent], cursor: Timestamp, authorized: Bool}
117
128
118
129
119-- ============================================
130-- ============================================
@@ -714,7 +725,7 @@ pollOnce = \token cursor -> do
714 let auth = authenticateSession sessions token t
725 let auth = authenticateSession sessions token t
715 case auth of
726 case auth of
716 Unauthorized {} ->
727 Unauthorized {} ->
717− yield PollUnauthorized {}
728+ yield {events: [], cursor: 0, authorized: False {}}
718 Authorized {pubkey} -> do
729 Authorized {pubkey} -> do
719 seqCounter <- *seqCounter
730 seqCounter <- *seqCounter
720 messages <- *messages
731 messages <- *messages
@@ -731,20 +742,19 @@ pollOnce = \token cursor -> do
731 encryptedBlob: m.encryptedBlob,
742 encryptedBlob: m.encryptedBlob,
732 timestamp: m.timestamp}}
743 timestamp: m.timestamp}}
733 let lastReceivedAt = fold (\acc m -> max acc m.receivedAt) clampedCursor limitedMsgs
744 let lastReceivedAt = fold (\acc m -> max acc m.receivedAt) clampedCursor limitedMsgs
734− yield PollDone {events: evts, cursor: lastReceivedAt}
745+ yield {events: evts, cursor: lastReceivedAt, authorized: True {}}
735
746
736−pollLoop : Text -> Int -> Int<Ms> -> IO {clock} PollResult
747+pollLoop : Text -> Int -> Int<Ms> -> IO {clock} PollResponse
737pollLoop = \token cursor deadline -> do
748pollLoop = \token cursor deadline -> do
738 result <- pollOnce token cursor
749 result <- pollOnce token cursor
739− case result of
750+ if not result.authorized then yield result
740− PollUnauthorized {} -> yield result
751+ else do
741− PollDone {events} -> do
742− t <- now
752+ t <- now
743− if count events == 0 && t < deadline
753+ if count result.events == 0 && t < deadline
744− then do
754+ then do
745− sleep (clamp 1 500 (deadline - t))
755+ sleep (clamp 1 500 (deadline - t))
746− pollLoop token cursor deadline
756+ pollLoop token cursor deadline
747− else yield result
757+ else yield result
748
758
749-- ============================================
759-- ============================================
750-- Request Handler
760-- Request Handler
@@ -817,9 +827,7 @@ serve = \req -> case req of
817 result <- pollLoop token cursor deadline
827 result <- pollLoop token cursor deadline
818 tAfterPoll <- now
828 tAfterPoll <- now
819 maybePruneAndBroadcast tAfterPoll
829 maybePruneAndBroadcast tAfterPoll
820− yield (respond (case result of
830+ yield (respond result)
821− PollUnauthorized {} -> {events: [], cursor: 0, authorized: False {}}
822− PollDone r -> {events: r.events, cursor: r.cursor, authorized: True {}}))
823
831
824 SendMessage {to, encryptedBlob, timestamp, authorization, respond} -> do
832 SendMessage {to, encryptedBlob, timestamp, authorization, respond} -> do
825 t <- now
833 t <- now
@@ -849,9 +857,9 @@ serve = \req -> case req of
849 forwards <- *forwards
857 forwards <- *forwards
850 let forwardServers = takeN remoteServers (maxForwardsEntries - count forwards)
858 let forwardServers = takeN remoteServers (maxForwardsEntries - count forwards)
851 let shouldForward = federationEnabled && count forwardServers > 0
859 let shouldForward = federationEnabled && count forwardServers > 0
852− let status = if localOnline then MsDelivered {}
860+ let status = if localOnline then Delivered {}
853− else if shouldForward then MsFederated {}
861+ else if shouldForward then Federated {}
854− else MsQueued {}
862+ else Queued {}
855 set *forwards = if shouldForward
863 set *forwards = if shouldForward
856 then union forwards (do s <- forwardServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
864 then union forwards (do s <- forwardServers; yield {messageId: msgId, toServer: s, createdAt: t, toKey: to, fromKey: fromKey, encryptedBlob: encryptedBlob, timestamp: clampedTs})
857 else forwards
865 else forwards
@@ -867,14 +875,14 @@ serve = \req -> case req of
867 authorization: federationAuth})
875 authorization: federationAuth})
868 whenOk fwdResult (removeForward msgId server))
876 whenOk fwdResult (removeForward msgId server))
869 case status of
877 case status of
870− MsQueued {} -> println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking")
878+ Queued {} -> println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking")
871 _ -> yield {})
879 _ -> yield {})
872 _ -> yield {}
880 _ -> yield {}
873 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
881 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
874 yield (case result.send of
882 yield (case result.send of
875 SendUnauthorized {} -> respond {status: MsUnauthorized {}, messageId: ""}
883 SendUnauthorized {} -> respond {status: MsUnauthorized {}, messageId: ""}
876 SendRejected {} -> respond {status: MsRejected {}, messageId: ""}
884 SendRejected {} -> respond {status: MsRejected {}, messageId: ""}
877− SendDone {status} -> respond {status: status, messageId: msgId})
885+ SendDone {status} -> respond {status: deliveryToStatus status, messageId: msgId})
878
886
879 AckMessages {messageIds, authorization, respond} -> do
887 AckMessages {messageIds, authorization, respond} -> do
880 t <- now
888 t <- now
@@ -884,7 +892,7 @@ serve = \req -> case req of
884 auth <- authenticateRequest authorization t
892 auth <- authenticateRequest authorization t
885 case auth of
893 case auth of
886 Unauthorized {} ->
894 Unauthorized {} ->
887− yield {pr: pr, ack: AckUnauthorized {}}
895+ yield {pr: pr, authorized: False {}, pendingAcks: [], evictedEntries: []}
888 Authorized {pubkey} -> do
896 Authorized {pubkey} -> do
889 messages <- *messages
897 messages <- *messages
890 let ackedMessages = do
898 let ackedMessages = do
@@ -902,16 +910,13 @@ serve = \req -> case req of
902 let evictedEntries = let excessCount = count combinedAckedIds - maxAckedIds in
910 let evictedEntries = let excessCount = count combinedAckedIds - maxAckedIds in
903 if excessCount <= 0 then [] else boundedSmallest combinedAckedIds excessCount (\a -> a.ackedAt)
911 if excessCount <= 0 then [] else boundedSmallest combinedAckedIds excessCount (\a -> a.ackedAt)
904 set *ackedIds = diff combinedAckedIds evictedEntries
912 set *ackedIds = diff combinedAckedIds evictedEntries
905− yield {pr: pr, ack: AckDone {pendingAcks: ackedFederated, evictedEntries: evictedEntries}}
913+ yield {pr: pr, authorized: True {}, pendingAcks: ackedFederated, evictedEntries: evictedEntries}
906 fork (do
914 fork (do
907− case result.ack of
908− AckDone {pendingAcks, evictedEntries} ->
909− when federationEnabled (do
915+ when (result.authorized && federationEnabled) (do
910− sendAcksAndClean pendingAcks
916+ sendAcksAndClean result.pendingAcks
911− retryEvictedAcks evictedEntries pendingAcks)
917+ retryEvictedAcks result.evictedEntries result.pendingAcks)
912− _ -> yield {}
913 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
918 broadcastOfflineGossip t result.pr.peerServers result.pr.expiredLocal)
914− yield (respond {ok: case result.ack of AckDone _ -> True {}; _ -> False {}})
919+ yield (respond {ok: result.authorized})
915
920
916 LookupPresence {pubkey, authorization, respond} -> do
921 LookupPresence {pubkey, authorization, respond} -> do
917 t <- now
922 t <- now
@@ -920,7 +925,7 @@ serve = \req -> case req of
920 auth <- authenticateRequest authorization t
925 auth <- authenticateRequest authorization t
921 case auth of
926 case auth of
922 Unauthorized {} ->
927 Unauthorized {} ->
923− yield {pr: pr, lookup: LookupUnauthorized {}}
928+ yield {pr: pr, server: "", authorized: False {}}
924 Authorized _ -> do
929 Authorized _ -> do
925 presence <- *presence
930 presence <- *presence
926 let valid = do
931 let valid = do
@@ -933,11 +938,9 @@ serve = \req -> case req of
933 if not (isLocalServer p.server) && dominated then Just {value: p} else acc) Nothing {} valid
938 if not (isLocalServer p.server) && dominated then Just {value: p} else acc) Nothing {} valid
934 let foundServer = if localOnline then serverHostN
939 let foundServer = if localOnline then serverHostN
935 else case bestRemote of Just {value: p} -> p.server; Nothing {} -> ""
940 else case bestRemote of Just {value: p} -> p.server; Nothing {} -> ""
936− yield {pr: pr, lookup: LookupDone {server: foundServer}}
941+ yield {pr: pr, server: foundServer, authorized: True {}}
937 forkBroadcastOffline t result.pr
942 forkBroadcastOffline t result.pr
938− yield (case result.lookup of
943+ yield (respond {server: result.server, authorized: result.authorized})
939− LookupUnauthorized {} -> respond {server: "", authorized: False {}}
940− LookupDone {server} -> respond {server: server, authorized: True {}})
941
944
942 RecvGossip {events, hopsRemaining, fromServer, authorization, respond} -> do
945 RecvGossip {events, hopsRemaining, fromServer, authorization, respond} -> do
943 t <- now
946 t <- now
@@ -999,9 +1002,9 @@ serve = \req -> case req of
999 messages <- *messages
1002 messages <- *messages
1000 ackedIds <- *ackedIds
1003 ackedIds <- *ackedIds
1001 if hasMessage messageId toKey messages || hasMessage messageId toKey ackedIds
1004 if hasMessage messageId toKey messages || hasMessage messageId toKey ackedIds
1002− then yield {pr: pr, forward: ForwardDuplicate {}}
1005+ then yield {pr: pr, ok: True {}}
1003 else if countMessagesFor messages toKey t >= maxMessagesPerRecipient
1006 else if countMessagesFor messages toKey t >= maxMessagesPerRecipient
1004− then yield {pr: pr, forward: ForwardFull {}}
1007+ then yield {pr: pr, ok: False {}}
1005 else do
1008 else do
1006 seqCounter <- *seqCounter
1009 seqCounter <- *seqCounter
1007 let seqTs = nextSeqTs t seqCounter
1010 let seqTs = nextSeqTs t seqCounter
@@ -1009,9 +1012,9 @@ serve = \req -> case req of
1009 set *messages = union messages
1012 set *messages = union messages
1010 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
1013 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
1011 encryptedBlob: encryptedBlob, timestamp: clampedTs, origin: FederatedOrigin {server: originServer}, receivedAt: seqTs}]
1014 encryptedBlob: encryptedBlob, timestamp: clampedTs, origin: FederatedOrigin {server: originServer}, receivedAt: seqTs}]
1012− yield {pr: pr, forward: ForwardStored {}}
1015+ yield {pr: pr, ok: True {}}
1013 forkBroadcastOffline t result.pr
1016 forkBroadcastOffline t result.pr
1014− yield (respond {ok: case result.forward of ForwardFull {} -> False {}; _ -> True {}})
1017+ yield (respond {ok: result.ok})
1015
1018
1016 FederationAck {messageIds, originServer, fromServer, authorization, respond} -> do
1019 FederationAck {messageIds, originServer, fromServer, authorization, respond} -> do
1017 t <- now
1020 t <- now