mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-17 15:46:44 +00:00
GODT-35: Finish all details and make tests pass
This commit is contained in:
@ -1,12 +1,35 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package pmapi
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
errCodeUpgradeApplication = 5003
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
@ -18,26 +41,34 @@ func (err Error) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func catchAPIError(_ *resty.Client, res *resty.Response) error {
|
||||
func (m *manager) catchAPIError(_ *resty.Client, res *resty.Response) error {
|
||||
if !res.IsError() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if res.StatusCode() == http.StatusUnauthorized {
|
||||
return ErrUnauthorized
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if apiErr, ok := res.Error().(*Error); ok {
|
||||
err = apiErr
|
||||
switch {
|
||||
case apiErr.Code == errCodeUpgradeApplication:
|
||||
err = ErrUpgradeApplication
|
||||
if m.cfg.UpgradeApplicationHandler != nil {
|
||||
m.cfg.UpgradeApplicationHandler()
|
||||
}
|
||||
case res.StatusCode() == http.StatusUnprocessableEntity:
|
||||
err = ErrUnprocessableEntity{apiErr}
|
||||
default:
|
||||
err = apiErr
|
||||
}
|
||||
} else {
|
||||
err = errors.New(res.Status())
|
||||
}
|
||||
|
||||
switch res.StatusCode() {
|
||||
case http.StatusUnauthorized:
|
||||
return errors.Wrap(ErrUnauthorized, err.Error())
|
||||
|
||||
default:
|
||||
return errors.Wrap(ErrAPIFailure, err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func catchRetryAfter(_ *resty.Client, res *resty.Response) (time.Duration, error) {
|
||||
@ -45,38 +76,47 @@ func catchRetryAfter(_ *resty.Client, res *resty.Response) (time.Duration, error
|
||||
if after := res.Header().Get("Retry-After"); after != "" {
|
||||
seconds, err := strconv.Atoi(after)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
log.WithError(err).Warning("Cannot convert Retry-After to number")
|
||||
seconds = 10
|
||||
}
|
||||
|
||||
// To avoid spikes when all clients retry at the same time, we add some random wait.
|
||||
seconds += rand.Intn(10) //nolint[gosec] It is OK to use weak random number generator here.
|
||||
|
||||
log.Warningf("Retrying %s after %ds induced by http code %d", res.Request.URL, seconds, res.StatusCode())
|
||||
return time.Duration(seconds) * time.Second, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 0 and no error means default behaviour which is exponential backoff with jitter.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func catchTooManyRequests(res *resty.Response, _ error) bool {
|
||||
func shouldRetry(res *resty.Response, err error) bool {
|
||||
if isRetryDisabled(res.Request.Context()) {
|
||||
return false
|
||||
}
|
||||
return isTooManyRequest(res) || isNoResponse(res, err)
|
||||
}
|
||||
|
||||
func isTooManyRequest(res *resty.Response) bool {
|
||||
return res.StatusCode() == http.StatusTooManyRequests
|
||||
}
|
||||
|
||||
func catchNoResponse(res *resty.Response, err error) bool {
|
||||
func isNoResponse(res *resty.Response, err error) bool {
|
||||
return res.RawResponse == nil && err != nil
|
||||
}
|
||||
|
||||
func catchProxyAvailable(res *resty.Response, err error) bool {
|
||||
/*
|
||||
if res.Request.Attempt < ... {
|
||||
return false
|
||||
}
|
||||
func wrapNoConnection(res *resty.Response, err error) (*resty.Response, error) {
|
||||
if err, ok := err.(*resty.ResponseError); ok {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if response is not empty {
|
||||
return false
|
||||
}
|
||||
if res.RawResponse != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if proxy is available {
|
||||
return true
|
||||
}
|
||||
*/
|
||||
|
||||
return false
|
||||
// Log useful information and return back nicer and clear error message.
|
||||
logrus.WithError(err).WithField("url", res.Request.URL).Warn("No internet connection")
|
||||
return res, ErrNoConnection
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user