Skip to content

Commit

Permalink
fix(GODT-2021): Indefinite parser stall after new line
Browse files Browse the repository at this point in the history
Can't consume new line or we will be stuck waiting on more data which
does not arrive until the client has received the response.
  • Loading branch information
LBeernaertProton authored and jameshoulahan committed Feb 16, 2023
1 parent 705f80f commit 9ee2b35
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
10 changes: 8 additions & 2 deletions imap/command/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,14 @@ func (p *Parser) Parse() (Command, error) {

result.Payload = payload

if err := p.parser.ConsumeNewLine(); err != nil {
return result, err
if err := p.parser.Consume(rfcparser.TokenTypeCR, "expected CR"); err != nil {
return Command{}, err
}

// Can't fully consume the last new line here or we will hang forever as the clients don't send the next token.
// In the next loop, the call to advance will ensure the next token in the stream gets loaded properly.
if !p.parser.Check(rfcparser.TokenTypeLF) {
return Command{}, p.parser.MakeError("expected LF after CR")
}

return result, nil
Expand Down
51 changes: 51 additions & 0 deletions imap/command/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,54 @@ func TestParser_LiteralWithContinuationSubmission(t *testing.T) {
require.NoError(t, err)
require.Equal(t, expected, cmd)
}

func TestParser_TwoCommandsInSuccession(t *testing.T) {
// Run one go routine that submits bytes until the continuation call has been received
continueCh := make(chan struct{})
reader, writer := io.Pipe()

go func() {
defer writer.Close()

firstLine := toIMAPLine(`A003 CAPABILITY`)

secondLine := toIMAPLine(`B002 LIST "" %`)

if l, err := writer.Write(firstLine); err != nil || l != len(firstLine) {
writer.CloseWithError(fmt.Errorf("failed to write first line: %w", err))
return
}

<-continueCh

if l, err := writer.Write(secondLine); err != nil || l != len(secondLine) {
writer.CloseWithError(fmt.Errorf("failed to write second line: %w", err))
return
}
}()

s := rfcparser.NewScanner(reader)
p := NewParser(s)

// First command
{
expected := Command{Tag: "A003", Payload: &Capability{}}

cmd, err := p.Parse()
require.NoError(t, err)
require.Equal(t, expected, cmd)
}

// Submit next command.
close(continueCh)
{
expected := Command{Tag: "B002", Payload: &List{
Mailbox: "",
ListMailbox: "%",
}}

cmd, err := p.Parse()
require.NoError(t, err)
require.Equal(t, expected, cmd)
}
}

0 comments on commit 9ee2b35

Please sign in to comment.