ooni-probe-cli/internal/engine/probeservices/benchselect.go
Simone Basso 273b70bacc
refactor: interfaces and data types into the model package (#642)
## Checklist

- [x] I have read the [contribution guidelines](https://github.com/ooni/probe-cli/blob/master/CONTRIBUTING.md)
- [x] reference issue for this pull request: https://github.com/ooni/probe/issues/1885
- [x] related ooni/spec pull request: N/A

Location of the issue tracker: https://github.com/ooni/probe

## Description

This PR contains a set of changes to move important interfaces and data types into the `./internal/model` package.

The criteria for including an interface or data type in here is roughly that the type should be important and used by several packages. We are especially interested to move more interfaces here to increase modularity.

An additional side effect is that, by reading this package, one should be able to understand more quickly how different parts of the codebase interact with each other.

This is what I want to move in `internal/model`:

- [x] most important interfaces from `internal/netxlite`
- [x] everything that was previously part of `internal/engine/model`
- [x] mocks from `internal/netxlite/mocks` should also be moved in here as a subpackage
2022-01-03 13:53:23 +01:00

149 lines
3.9 KiB
Go

package probeservices
import (
"context"
"time"
"github.com/ooni/probe-cli/v3/internal/model"
)
// Default returns the default probe services
func Default() []model.OOAPIService {
return []model.OOAPIService{{
Address: "https://ps1.ooni.io",
Type: "https",
}, {
Address: "https://ps2.ooni.io",
Type: "https",
}, {
Front: "dkyhjv0wpi2dk.cloudfront.net",
Type: "cloudfront",
Address: "https://dkyhjv0wpi2dk.cloudfront.net",
}}
}
// SortEndpoints gives priority to https, then cloudfronted, then onion.
func SortEndpoints(in []model.OOAPIService) (out []model.OOAPIService) {
for _, entry := range in {
if entry.Type == "https" {
out = append(out, entry)
}
}
for _, entry := range in {
if entry.Type == "cloudfront" {
out = append(out, entry)
}
}
for _, entry := range in {
if entry.Type == "onion" {
out = append(out, entry)
}
}
return
}
// OnlyHTTPS returns the HTTPS endpoints only.
func OnlyHTTPS(in []model.OOAPIService) (out []model.OOAPIService) {
for _, entry := range in {
if entry.Type == "https" {
out = append(out, entry)
}
}
return
}
// OnlyFallbacks returns the fallback endpoints only.
func OnlyFallbacks(in []model.OOAPIService) (out []model.OOAPIService) {
for _, entry := range SortEndpoints(in) {
if entry.Type != "https" {
out = append(out, entry)
}
}
return
}
// Candidate is a candidate probe service.
type Candidate struct {
// Duration is the time it took to access the service.
Duration time.Duration
// Err indicates whether the service works.
Err error
// Endpoint is the service endpoint.
Endpoint model.OOAPIService
// TestHelpers contains the data returned by the endpoint.
TestHelpers map[string][]model.OOAPIService
}
func (c *Candidate) try(ctx context.Context, sess Session) {
client, err := NewClient(sess, c.Endpoint)
if err != nil {
c.Err = err
return
}
start := time.Now()
testhelpers, err := client.GetTestHelpers(ctx)
c.Duration = time.Since(start)
c.Err = err
c.TestHelpers = testhelpers
sess.Logger().Debugf("probe services: %+v: %+v %s", c.Endpoint, err, c.Duration)
}
func try(ctx context.Context, sess Session, svc model.OOAPIService) *Candidate {
candidate := &Candidate{Endpoint: svc}
candidate.try(ctx, sess)
return candidate
}
// TryAll tries all the input services using the provided context and session. It
// returns a list containing information on each candidate that was tried. We will
// try all the HTTPS candidates first. So, the beginning of the list will contain
// all of them, and for each of them you will know whether it worked (by checking the
// Err field) and how fast it was (by checking the Duration field). You should pick
// the fastest one that worked. If none of them works, then TryAll will subsequently
// attempt with all the available fallbacks, and return at the first success. In
// such case, you will see a list of N failing HTTPS candidates, followed by a single
// successful fallback candidate (e.g. cloudfronted). If all candidates fail, you
// see in output a list containing all entries where Err is not nil.
func TryAll(ctx context.Context, sess Session, in []model.OOAPIService) (out []*Candidate) {
var found bool
for _, svc := range OnlyHTTPS(in) {
candidate := try(ctx, sess, svc)
out = append(out, candidate)
if candidate.Err == nil {
found = true
}
}
if !found {
for _, svc := range OnlyFallbacks(in) {
candidate := try(ctx, sess, svc)
out = append(out, candidate)
if candidate.Err == nil {
return
}
}
}
return
}
// SelectBest selects the best among the candidates. If there is no
// suitable candidate, then this function returns nil.
func SelectBest(candidates []*Candidate) (selected *Candidate) {
for _, e := range candidates {
if e.Err != nil {
continue
}
if selected == nil {
selected = e
continue
}
if selected.Duration > e.Duration {
selected = e
continue
}
}
return
}