imapserver: implement "inprogress" response code (RFC 9585) for keepalive during long search

For long searches in big mailboxes, without any matches, we would previously
keep working and not say anything. Clients could interpret this silence as a
broken connection at some point. We now send a "we're still searching" untagged
OK responses with code INPROGRESS every 10 seconds while we're still searching,
to prevent the client from closing the connection. We also send how many
messages we've processed, and usually also how many we need to process in grand
total. Clients can use this to show a progress bar.
This commit is contained in:
Mechiel Lukkien
2025-03-30 10:29:15 +02:00
parent 3e128d744e
commit cc5e3165ea
6 changed files with 112 additions and 4 deletions

View File

@ -260,6 +260,31 @@ func TestSearch(t *testing.T) {
tc.transactf("ok", `search charset utf-8 text "mox"`)
tc.xsearch(2, 3)
// Check for properly formed INPROGRESS response code.
orig := inProgressPeriod
inProgressPeriod = 0
tc.cmdf("tag1", "search undraft")
tc.response("ok")
inprogress := func(cur, goal uint32) imapclient.UntaggedResult {
return imapclient.UntaggedResult{
Status: "OK",
RespText: imapclient.RespText{
Code: "INPROGRESS",
CodeArg: imapclient.CodeInProgress{Tag: "tag1", Current: &cur, Goal: &goal},
More: "still searching",
},
}
}
tc.xuntagged(
imapclient.UntaggedSearch([]uint32{1, 2}),
// Due to inProgressPeriod 0, we get an inprogress response for each message in the mailbox.
inprogress(0, 3),
inprogress(1, 3),
inprogress(2, 3),
)
inProgressPeriod = orig
esearchall := func(ss string) imapclient.UntaggedEsearch {
return imapclient.UntaggedEsearch{All: esearchall0(ss)}
}