Mitigate Apple Mail re-sync (both bodies and meta info)

This commit is contained in:
Michal Horejsek
2020-12-22 12:06:19 +01:00
parent 5117672388
commit 516ca018d3
24 changed files with 239 additions and 225 deletions

View File

@ -103,8 +103,9 @@ type ClientConfig struct {
// Zero means no limitation.
MinBytesPerSecond int64
NoConnectionHandler func()
ConnectionHandler func()
ConnectionOnHandler func()
ConnectionOffHandler func()
UpgradeApplicationHandler func()
}
// client is a client of the protonmail API. It implements the Client interface.
@ -265,6 +266,7 @@ func (c *client) doBuffered(req *http.Request, bodyBuffer []byte, retryUnauthori
if res == nil {
c.log.WithError(err).Error("Cannot get response")
err = ErrAPINotReachable
c.cm.noConnection()
}
return
}
@ -405,6 +407,12 @@ func (c *client) doJSONBuffered(req *http.Request, reqBodyBuffer []byte, data in
}
return c.doJSONBuffered(req, reqBodyBuffer, data)
}
if errCode.Err() == ErrAPINotReachable {
c.cm.noConnection()
}
if errCode.Err() == ErrUpgradeApplication {
c.cm.config.UpgradeApplicationHandler()
}
}
if err := json.Unmarshal(resBody, data); err != nil {

View File

@ -31,7 +31,7 @@ import (
const maxLogoutRetries = 5
// ClientManager is a manager of clients.
type ClientManager struct {
type ClientManager struct { //nolint[maligned]
// newClient is used to create new Clients. By default this creates pmapi clients but it can be overridden to
// create other types of clients (e.g. for integration tests).
newClient func(userID string) Client
@ -61,6 +61,8 @@ type ClientManager struct {
idGen idGen
connectionOff bool
log *logrus.Entry
}
@ -108,6 +110,8 @@ func NewClientManager(config *ClientConfig) (cm *ClientManager) {
proxyProvider: newProxyProvider(dohProviders, proxyQuery),
proxyUseDuration: proxyUseDuration,
connectionOff: false,
log: logrus.WithField("pkg", "pmapi-manager"),
}
@ -121,6 +125,30 @@ func NewClientManager(config *ClientConfig) (cm *ClientManager) {
return cm
}
func (cm *ClientManager) noConnection() {
cm.log.Trace("No connection available")
if cm.connectionOff {
return
}
cm.log.Warn("Connection lost")
cm.config.ConnectionOffHandler()
cm.connectionOff = true
go func() {
for {
time.Sleep(30 * time.Second)
if err := cm.CheckConnection(); err == nil {
cm.log.Info("Connection re-established")
cm.config.ConnectionOnHandler()
cm.connectionOff = false
return
}
}
}()
}
// SetClientConstructor sets the method used to construct clients.
// By default this is `pmapi.newClient` but can be overridden with this method.
func (cm *ClientManager) SetClientConstructor(f func(userID string) Client) {

View File

@ -37,17 +37,7 @@ func NewProxyTLSDialer(dialer TLSDialer, cm *ClientManager) *ProxyTLSDialer {
}
// DialTLS dials the given network/address. If it fails, it retries using a proxy.
func (d *ProxyTLSDialer) DialTLS(network, address string) (net.Conn, error) {
conn, err := d.dialTLS(network, address)
if err != nil {
d.cm.config.NoConnectionHandler()
} else {
d.cm.config.ConnectionHandler()
}
return conn, err
}
func (d *ProxyTLSDialer) dialTLS(network, address string) (conn net.Conn, err error) {
func (d *ProxyTLSDialer) DialTLS(network, address string) (conn net.Conn, err error) {
if conn, err = d.dialer.DialTLS(network, address); err == nil {
return
}