mirror of
https://github.com/safing/portmaster
synced 2025-04-23 12:29:10 +00:00
108 lines
2.8 KiB
Go
108 lines
2.8 KiB
Go
package docks
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/safing/portmaster/spn/hub"
|
|
"github.com/safing/portmaster/spn/ships"
|
|
"github.com/safing/portmaster/spn/terminal"
|
|
)
|
|
|
|
// Measurement Configuration.
|
|
const (
|
|
CraneMeasurementTTLDefault = 30 * time.Minute
|
|
CraneMeasurementTTLByCostBase = 1 * time.Minute
|
|
CraneMeasurementTTLByCostMin = 30 * time.Minute
|
|
CraneMeasurementTTLByCostMax = 3 * time.Hour
|
|
|
|
// With a base TTL of 1m, this leads to:
|
|
// 20c -> 20m -> raised to 30m
|
|
// 50c -> 50m
|
|
// 100c -> 1h40m
|
|
// 1000c -> 16h40m -> capped to 3h.
|
|
)
|
|
|
|
// MeasureHub measures the connection to this Hub and saves the results to the
|
|
// Hub.
|
|
func MeasureHub(ctx context.Context, h *hub.Hub, checkExpiryWith time.Duration) *terminal.Error {
|
|
// Check if we are measuring before building a connection.
|
|
if capacityTestRunning.IsSet() {
|
|
return terminal.ErrTryAgainLater.With("another capacity op is already running")
|
|
}
|
|
|
|
// Check if we have a connection to this Hub.
|
|
crane := GetAssignedCrane(h.ID)
|
|
if crane == nil {
|
|
// Connect to Hub.
|
|
var err error
|
|
crane, err = establishCraneForMeasuring(ctx, h)
|
|
if err != nil {
|
|
return terminal.ErrConnectionError.With("failed to connect to %s: %s", h, err)
|
|
}
|
|
// Stop crane if established just for measuring.
|
|
defer crane.Stop(nil)
|
|
}
|
|
|
|
// Run latency test.
|
|
_, expires := h.GetMeasurements().GetLatency()
|
|
if checkExpiryWith == 0 || time.Now().Add(-checkExpiryWith).After(expires) {
|
|
latOp, tErr := NewLatencyTestOp(crane.Controller)
|
|
if !tErr.IsOK() {
|
|
return tErr
|
|
}
|
|
select {
|
|
case tErr = <-latOp.Result():
|
|
if !tErr.IsOK() {
|
|
return tErr
|
|
}
|
|
case <-ctx.Done():
|
|
return terminal.ErrCanceled
|
|
case <-time.After(1 * time.Minute):
|
|
crane.Controller.StopOperation(latOp, terminal.ErrTimeout)
|
|
return terminal.ErrTimeout.With("waiting for latency test")
|
|
}
|
|
}
|
|
|
|
// Run capacity test.
|
|
_, expires = h.GetMeasurements().GetCapacity()
|
|
if checkExpiryWith == 0 || time.Now().Add(-checkExpiryWith).After(expires) {
|
|
capOp, tErr := NewCapacityTestOp(crane.Controller, nil)
|
|
if !tErr.IsOK() {
|
|
return tErr
|
|
}
|
|
select {
|
|
case tErr = <-capOp.Result():
|
|
if !tErr.IsOK() {
|
|
return tErr
|
|
}
|
|
case <-ctx.Done():
|
|
return terminal.ErrCanceled
|
|
case <-time.After(1 * time.Minute):
|
|
crane.Controller.StopOperation(capOp, terminal.ErrTimeout)
|
|
return terminal.ErrTimeout.With("waiting for capacity test")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func establishCraneForMeasuring(ctx context.Context, dst *hub.Hub) (*Crane, error) {
|
|
ship, err := ships.Launch(ctx, dst, nil, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to launch ship: %w", err)
|
|
}
|
|
|
|
crane, err := NewCrane(ship, dst, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create crane: %w", err)
|
|
}
|
|
|
|
err = crane.Start(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to start crane: %w", err)
|
|
}
|
|
|
|
return crane, nil
|
|
}
|