Skip to content

Commit 9a2351a

Browse files
committed
imapserver: split command handling
Split into two parts: parsing and processing. Some commands are incompatible with this model (e.g. APPEND), so will have a no-op second step. The goal is to parallelize the second step with other processing: reading the next command, and eventually handling the next command as well.
1 parent 283be86 commit 9a2351a

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

imapserver/conn.go

+37-2
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,10 @@ func (c *Conn) readCommand(dec *imapwire.Decoder) error {
202202

203203
// TODO: handle multiple commands concurrently
204204
sendOK := true
205-
var err error
205+
var (
206+
err error
207+
cmd *command
208+
)
206209
switch name {
207210
case "NOOP", "CHECK":
208211
err = c.handleNoop(dec)
@@ -252,7 +255,7 @@ func (c *Conn) readCommand(dec *imapwire.Decoder) error {
252255
err = c.handleAppend(tag, dec)
253256
sendOK = false
254257
case "FETCH", "UID FETCH":
255-
err = c.handleFetch(dec, numKind)
258+
cmd, err = c.handleFetch(dec, numKind)
256259
case "EXPUNGE":
257260
err = c.handleExpunge(dec)
258261
case "UID EXPUNGE":
@@ -282,6 +285,10 @@ func (c *Conn) readCommand(dec *imapwire.Decoder) error {
282285

283286
dec.DiscardLine()
284287

288+
if err == nil && cmd != nil {
289+
err = cmd.Wait()
290+
}
291+
285292
var (
286293
resp *imap.StatusResponse
287294
imapErr *imap.Error
@@ -474,6 +481,34 @@ func (c *Conn) poll(cmd string) error {
474481
return c.session.Poll(w, allowExpunge)
475482
}
476483

484+
type command struct {
485+
done chan struct{}
486+
err error
487+
}
488+
489+
func newCommand(f func() error) *command {
490+
cmd := &command{done: make(chan struct{})}
491+
go cmd.run(f)
492+
return cmd
493+
}
494+
495+
func (cmd *command) run(f func() error) {
496+
var err error
497+
defer func() {
498+
if v := recover(); v != nil {
499+
err = fmt.Errorf("panic handling command: %v\n%s", v, debug.Stack())
500+
}
501+
cmd.err = err
502+
close(cmd.done)
503+
}()
504+
err = f()
505+
}
506+
507+
func (cmd *command) Wait() error {
508+
<-cmd.done
509+
return cmd.err
510+
}
511+
477512
type responseEncoder struct {
478513
*imapwire.Encoder
479514
conn *Conn

imapserver/fetch.go

+10-11
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ type fetchWriterOptions struct {
2323
obsolete map[*imap.FetchItemBodySection]string
2424
}
2525

26-
func (c *Conn) handleFetch(dec *imapwire.Decoder, numKind NumKind) error {
26+
func (c *Conn) handleFetch(dec *imapwire.Decoder, numKind NumKind) (*command, error) {
2727
var seqSet imap.SeqSet
2828
if !dec.ExpectSP() || !dec.ExpectSeqSet(&seqSet) || !dec.ExpectSP() {
29-
return dec.Err()
29+
return nil, dec.Err()
3030
}
3131

3232
var options imap.FetchOptions
@@ -43,12 +43,12 @@ func (c *Conn) handleFetch(dec *imapwire.Decoder, numKind NumKind) error {
4343
return handleFetchAtt(dec, name, &options, &writerOptions)
4444
})
4545
if err != nil {
46-
return err
46+
return nil, err
4747
}
4848
if !isList {
4949
name, err := readFetchAttName(dec)
5050
if err != nil {
51-
return err
51+
return nil, err
5252
}
5353

5454
// Handle macros
@@ -70,28 +70,27 @@ func (c *Conn) handleFetch(dec *imapwire.Decoder, numKind NumKind) error {
7070
handleFetchBodyStructure(&options, &writerOptions, false)
7171
default:
7272
if err := handleFetchAtt(dec, name, &options, &writerOptions); err != nil {
73-
return err
73+
return nil, err
7474
}
7575
}
7676
}
7777

7878
if !dec.ExpectCRLF() {
79-
return dec.Err()
79+
return nil, dec.Err()
8080
}
8181

8282
if err := c.checkState(imap.ConnStateSelected); err != nil {
83-
return err
83+
return nil, err
8484
}
8585

8686
if numKind == NumKindUID {
8787
options.UID = true
8888
}
8989

9090
w := &FetchWriter{conn: c, options: writerOptions}
91-
if err := c.session.Fetch(w, numKind, seqSet, &options); err != nil {
92-
return err
93-
}
94-
return nil
91+
return newCommand(func() error {
92+
return c.session.Fetch(w, numKind, seqSet, &options)
93+
}), nil
9594
}
9695

9796
func handleFetchAtt(dec *imapwire.Decoder, attName string, options *imap.FetchOptions, writerOptions *fetchWriterOptions) error {

0 commit comments

Comments
 (0)