From 4e3ad4f7fad565db21d88e0586d51c25bd5c0c13 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Tue, 16 May 2023 09:53:41 +0200 Subject: [PATCH] fix(GODT-2626): Server Events should not be merged. d18e5932b28f83b201709a04fb7b8c6f74003574 Includes GPA bump: https://github.com/ProtonMail/go-proton-api/pull/80 --- go.mod | 2 +- go.sum | 2 + internal/user/user.go | 122 +++++++++++++++++++++--------------------- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/go.mod b/go.mod index 158c41fb..2712e430 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/ProtonMail/gluon v0.16.1-0.20230516073349-d18e5932b28f github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a - github.com/ProtonMail/go-proton-api v0.4.1-0.20230505091503-167f3d239b0c + github.com/ProtonMail/go-proton-api v0.4.1-0.20230516070548-faf4f87bf9e7 github.com/ProtonMail/gopenpgp/v2 v2.7.1-proton github.com/PuerkitoBio/goquery v1.8.1 github.com/abiosoft/ishell v2.0.0+incompatible diff --git a/go.sum b/go.sum index 15e57690..7b03b86b 100644 --- a/go.sum +++ b/go.sum @@ -41,6 +41,8 @@ github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ek github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/go-proton-api v0.4.1-0.20230505091503-167f3d239b0c h1:uqo3mKt4ffhqPFLVV7VxjuN12DAFQmqEju/Wy5dk6Rk= github.com/ProtonMail/go-proton-api v0.4.1-0.20230505091503-167f3d239b0c/go.mod h1:UkrG9gN2o9mzdx/an0XRc6a4s5Haef1A7Eyd2iXlw28= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230516070548-faf4f87bf9e7 h1:7aY4azqc8PzYtg4+xG7b9wBEnckrl7rVMlMoFMWRkdA= +github.com/ProtonMail/go-proton-api v0.4.1-0.20230516070548-faf4f87bf9e7/go.mod h1:UkrG9gN2o9mzdx/an0XRc6a4s5Haef1A7Eyd2iXlw28= github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg= github.com/ProtonMail/go-srp v0.0.5/go.mod h1:06iYHtLXW8vjLtccWj++x3MKy65sIT8yZd7nrJF49rs= github.com/ProtonMail/gopenpgp/v2 v2.7.1-proton h1:YS6M20yvjCJPR1r4ADW5TPn6rahs4iAyZaACei86bEc= diff --git a/internal/user/user.go b/internal/user/user.go index 3e601b9c..8376fb61 100644 --- a/internal/user/user.go +++ b/internal/user/user.go @@ -690,87 +690,89 @@ func (user *User) doEventPoll(ctx context.Context) error { user.eventLock.Lock() defer user.eventLock.Unlock() - event, more, err := user.client.GetEvent(ctx, user.vault.EventID()) + gpaEvents, more, err := user.client.GetEvent(ctx, user.vault.EventID()) if err != nil { return fmt.Errorf("failed to get event (caused by %T): %w", internal.ErrCause(err), err) } // If the event ID hasn't changed, there are no new events. - if event.EventID == user.vault.EventID() { + if gpaEvents[len(gpaEvents)-1].EventID == user.vault.EventID() { user.log.Debug("No new API events") return nil } - user.log.WithFields(logrus.Fields{ - "old": user.vault.EventID(), - "new": event, - }).Info("Received new API event") + for _, event := range gpaEvents { + user.log.WithFields(logrus.Fields{ + "old": user.vault.EventID(), + "new": event, + }).Info("Received new API event") - // Handle the event. - if err := user.handleAPIEvent(ctx, event); err != nil { - // If the error is a context cancellation, return error to retry later. - if errors.Is(err, context.Canceled) { - return fmt.Errorf("failed to handle event due to context cancellation: %w", err) + // Handle the event. + if err := user.handleAPIEvent(ctx, event); err != nil { + // If the error is a context cancellation, return error to retry later. + if errors.Is(err, context.Canceled) { + return fmt.Errorf("failed to handle event due to context cancellation: %w", err) + } + + // If the error is a network error, return error to retry later. + if netErr := new(proton.NetError); errors.As(err, &netErr) { + return fmt.Errorf("failed to handle event due to network issue: %w", err) + } + + // Catch all for uncategorized net errors that may slip through. + if netErr := new(net.OpError); errors.As(err, &netErr) { + return fmt.Errorf("failed to handle event due to network issues (uncategorized): %w", err) + } + + // In case a json decode error slips through. + if jsonErr := new(json.UnmarshalTypeError); errors.As(err, &jsonErr) { + user.eventCh.Enqueue(events.UncategorizedEventError{ + UserID: user.ID(), + Error: err, + }) + + return fmt.Errorf("failed to handle event due to JSON issue: %w", err) + } + + // If the error is an unexpected EOF, return error to retry later. + if errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("failed to handle event due to EOF: %w", err) + } + + // If the error is a server-side issue, return error to retry later. + if apiErr := new(proton.APIError); errors.As(err, &apiErr) && apiErr.Status >= 500 { + return fmt.Errorf("failed to handle event due to server error: %w", err) + } + + // Otherwise, the error is a client-side issue; notify bridge to handle it. + user.log.WithField("event", event).Warn("Failed to handle API event") + + user.eventCh.Enqueue(events.UserBadEvent{ + UserID: user.ID(), + OldEventID: user.vault.EventID(), + NewEventID: event.EventID, + EventInfo: event.String(), + Error: err, + }) + + return fmt.Errorf("failed to handle event due to client error: %w", err) } - // If the error is a network error, return error to retry later. - if netErr := new(proton.NetError); errors.As(err, &netErr) { - return fmt.Errorf("failed to handle event due to network issue: %w", err) - } + user.log.WithField("event", event).Debug("Handled API event") - // Catch all for uncategorized net errors that may slip through. - if netErr := new(net.OpError); errors.As(err, &netErr) { - return fmt.Errorf("failed to handle event due to network issues (uncategorized): %w", err) - } - - // In case a json decode error slips through. - if jsonErr := new(json.UnmarshalTypeError); errors.As(err, &jsonErr) { - user.eventCh.Enqueue(events.UncategorizedEventError{ + // Update the event ID in the vault. If this fails, notify bridge to handle it. + if err := user.vault.SetEventID(event.EventID); err != nil { + user.eventCh.Enqueue(events.UserBadEvent{ UserID: user.ID(), Error: err, }) - return fmt.Errorf("failed to handle event due to JSON issue: %w", err) + return fmt.Errorf("failed to update event ID: %w", err) } - // If the error is an unexpected EOF, return error to retry later. - if errors.Is(err, io.ErrUnexpectedEOF) { - return fmt.Errorf("failed to handle event due to EOF: %w", err) - } - - // If the error is a server-side issue, return error to retry later. - if apiErr := new(proton.APIError); errors.As(err, &apiErr) && apiErr.Status >= 500 { - return fmt.Errorf("failed to handle event due to server error: %w", err) - } - - // Otherwise, the error is a client-side issue; notify bridge to handle it. - user.log.WithField("event", event).Warn("Failed to handle API event") - - user.eventCh.Enqueue(events.UserBadEvent{ - UserID: user.ID(), - OldEventID: user.vault.EventID(), - NewEventID: event.EventID, - EventInfo: event.String(), - Error: err, - }) - - return fmt.Errorf("failed to handle event due to client error: %w", err) + user.log.WithField("eventID", event.EventID).Debug("Updated event ID in vault") } - user.log.WithField("event", event).Debug("Handled API event") - - // Update the event ID in the vault. If this fails, notify bridge to handle it. - if err := user.vault.SetEventID(event.EventID); err != nil { - user.eventCh.Enqueue(events.UserBadEvent{ - UserID: user.ID(), - Error: err, - }) - - return fmt.Errorf("failed to update event ID: %w", err) - } - - user.log.WithField("eventID", event.EventID).Debug("Updated event ID in vault") - if more { user.goPollAPIEvents(false) }