mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 04:36:43 +00:00
feat: clientmanager has checkconnection
This commit is contained in:
@ -1,90 +0,0 @@
|
||||
// Copyright (c) 2020 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 connection
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/pkg/logs"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
)
|
||||
|
||||
// Errors for possible connection issues
|
||||
var (
|
||||
ErrNoInternetConnection = errors.New("no internet connection")
|
||||
ErrCanNotReachAPI = errors.New("can not reach PM API")
|
||||
log = logs.GetLogEntry("connection") //nolint[gochecknoglobals]
|
||||
)
|
||||
|
||||
// CheckInternetConnection does a check of API connection. It checks two of our endpoints in parallel.
|
||||
// One endpoint is part of the protonmail API, while the other is not.
|
||||
// This allows us to determine whether there is a problem with the connection itself or only a problem with our API.
|
||||
// Two errors can be returned, ErrNoInternetConnection or ErrCanNotReachAPI.
|
||||
func CheckInternetConnection() error {
|
||||
client := &http.Client{
|
||||
// TODO: Set transport properly! (Need access to ClientManager somehow)
|
||||
// Transport: pmapi.NewDialerWithPinning(pmapi.CurrentUserAgent).TransportWithPinning(),
|
||||
}
|
||||
|
||||
// Do not cumulate timeouts, use goroutines.
|
||||
retStatus := make(chan error)
|
||||
retAPI := make(chan error)
|
||||
|
||||
// Check protonstatus.com without SSL for performance reasons. vpn_status endpoint is fast and
|
||||
// returns only OK; this endpoint is not known by the public. We check the connection only.
|
||||
go checkConnection(client, "http://protonstatus.com/vpn_status", retStatus)
|
||||
|
||||
// Check of API reachability also uses a fast endpoint.
|
||||
// TODO: This should check active proxy, not the RootURL
|
||||
go checkConnection(client, pmapi.RootURL+"/tests/ping", retAPI)
|
||||
|
||||
errStatus := <-retStatus
|
||||
errAPI := <-retAPI
|
||||
|
||||
if errStatus != nil {
|
||||
if errAPI != nil {
|
||||
log.Error("Checking internet connection failed with ", errStatus, " and ", errAPI)
|
||||
return ErrNoInternetConnection
|
||||
}
|
||||
log.Warning("API OK, but status: ", errStatus)
|
||||
return nil
|
||||
}
|
||||
|
||||
if errAPI != nil {
|
||||
log.Error("Status OK, but API: ", errAPI)
|
||||
return ErrCanNotReachAPI
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkConnection(client *http.Client, url string, errorChannel chan error) {
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
errorChannel <- err
|
||||
return
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
errorChannel <- fmt.Errorf("HTTP status code %d", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
errorChannel <- nil
|
||||
}
|
||||
@ -15,7 +15,7 @@
|
||||
// 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 connection
|
||||
package pmapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
@ -255,6 +255,68 @@ func (cm *ClientManager) GetClientAuthChannel() chan ClientAuth {
|
||||
return cm.clientAuths
|
||||
}
|
||||
|
||||
// Errors for possible connection issues
|
||||
var (
|
||||
ErrNoInternetConnection = errors.New("no internet connection")
|
||||
ErrCanNotReachAPI = errors.New("can not reach PM API")
|
||||
)
|
||||
|
||||
// CheckConnection returns an error if there is no internet connection.
|
||||
// This should be moved to the ConnectionManager when it is implemented.
|
||||
func (cm *ClientManager) CheckConnection() error {
|
||||
client := getHTTPClient(cm.config, cm.roundTripper)
|
||||
|
||||
// Do not cumulate timeouts, use goroutines.
|
||||
retStatus := make(chan error)
|
||||
retAPI := make(chan error)
|
||||
|
||||
// Check protonstatus.com without SSL for performance reasons. vpn_status endpoint is fast and
|
||||
// returns only OK; this endpoint is not known by the public. We check the connection only.
|
||||
go checkConnection(client, "http://protonstatus.com/vpn_status", retStatus)
|
||||
|
||||
// Check of API reachability also uses a fast endpoint.
|
||||
go checkConnection(client, cm.GetRootURL()+"/tests/ping", retAPI)
|
||||
|
||||
errStatus := <-retStatus
|
||||
errAPI := <-retAPI
|
||||
|
||||
switch {
|
||||
case errStatus == nil && errAPI == nil:
|
||||
return nil
|
||||
|
||||
case errStatus == nil && errAPI != nil:
|
||||
cm.log.Error("ProtonStatus is reachable but API is not")
|
||||
return ErrCanNotReachAPI
|
||||
|
||||
case errStatus != nil && errAPI == nil:
|
||||
cm.log.Warn("API is reachable but protonstatus is not")
|
||||
return nil
|
||||
|
||||
case errStatus != nil && errAPI != nil:
|
||||
cm.log.Error("Both ProtonStatus and API are unreachable")
|
||||
return ErrNoInternetConnection
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkConnection(client *http.Client, url string, errorChannel chan error) {
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
errorChannel <- err
|
||||
return
|
||||
}
|
||||
|
||||
_ = resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
errorChannel <- fmt.Errorf("HTTP status code %d", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
|
||||
errorChannel <- nil
|
||||
}
|
||||
|
||||
// forwardClientAuths handles all incoming auths from clients before forwarding them on the bridge auth channel.
|
||||
func (cm *ClientManager) forwardClientAuths() {
|
||||
for auth := range cm.clientAuths {
|
||||
@ -266,7 +328,7 @@ func (cm *ClientManager) forwardClientAuths() {
|
||||
}
|
||||
|
||||
// SetTokenIfUnset sets the token for the given userID if it wasn't already set.
|
||||
// The token does not expire.
|
||||
// The set token does not expire.
|
||||
func (cm *ClientManager) SetTokenIfUnset(userID, token string) {
|
||||
cm.tokensLocker.Lock()
|
||||
defer cm.tokensLocker.Unlock()
|
||||
@ -352,6 +414,7 @@ func (cm *ClientManager) handleClientAuth(ca ClientAuth) {
|
||||
cm.setToken(ca.UserID, ca.Auth.GenToken(), time.Duration(ca.Auth.ExpiresIn)*time.Second)
|
||||
}
|
||||
|
||||
// watchTokenExpirations refreshes any tokens which are about to expire.
|
||||
func (cm *ClientManager) watchTokenExpirations() {
|
||||
for userID := range cm.expiredTokens {
|
||||
log := cm.log.WithField("userID", userID)
|
||||
|
||||
Reference in New Issue
Block a user