This diff makes the implementation of the engine package more abstract by changing HTTPClient() to return a model.HTTPClient as opposed to returning an *http.Client. Part of https://github.com/ooni/probe/issues/2184
		
			
				
	
	
		
			128 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			128 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Package probeservices contains code to contact OONI probe services.
 | 
						|
//
 | 
						|
// The probe services are HTTPS endpoints distributed across a bunch of data
 | 
						|
// centres implementing a bunch of OONI APIs. When started, OONI will benchmark
 | 
						|
// the available probe services and select the fastest one. Eventually all the
 | 
						|
// possible OONI APIs will run as probe services.
 | 
						|
//
 | 
						|
// This package implements the following APIs:
 | 
						|
//
 | 
						|
// 1. v2.0.0 of the OONI bouncer specification defined
 | 
						|
// in https://github.com/ooni/spec/blob/master/backends/bk-004-bouncer;
 | 
						|
//
 | 
						|
// 2. v2.0.0 of the OONI collector specification defined
 | 
						|
// in https://github.com/ooni/spec/blob/master/backends/bk-003-collector.md;
 | 
						|
//
 | 
						|
// 3. most of the OONI orchestra API: login, register, fetch URLs for
 | 
						|
// the Web Connectivity experiment, input for Tor and Psiphon.
 | 
						|
//
 | 
						|
// Orchestra is a set of OONI APIs for probe orchestration. We currently mainly
 | 
						|
// using it for fetching inputs for the tor, psiphon, and web experiments.
 | 
						|
//
 | 
						|
// In addition, this package also contains code to benchmark the available
 | 
						|
// probe services, discard non working ones, select the fastest.
 | 
						|
package probeservices
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"net/url"
 | 
						|
 | 
						|
	"github.com/ooni/probe-cli/v3/internal/atomicx"
 | 
						|
	"github.com/ooni/probe-cli/v3/internal/httpx"
 | 
						|
	"github.com/ooni/probe-cli/v3/internal/model"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// ErrUnsupportedEndpoint indicates that we don't support this endpoint type.
 | 
						|
	ErrUnsupportedEndpoint = errors.New("probe services: unsupported endpoint type")
 | 
						|
 | 
						|
	// ErrUnsupportedCloudFrontAddress indicates that we don't support this
 | 
						|
	// cloudfront address (e.g. wrong scheme, presence of port).
 | 
						|
	ErrUnsupportedCloudFrontAddress = errors.New(
 | 
						|
		"probe services: unsupported cloud front address",
 | 
						|
	)
 | 
						|
 | 
						|
	// ErrNotRegistered indicates that the probe is not registered
 | 
						|
	// with the OONI orchestra backend.
 | 
						|
	ErrNotRegistered = errors.New("not registered")
 | 
						|
 | 
						|
	// ErrNotLoggedIn indicates that we are not logged in
 | 
						|
	ErrNotLoggedIn = errors.New("not logged in")
 | 
						|
 | 
						|
	// ErrInvalidMetadata indicates that the metadata is not valid
 | 
						|
	ErrInvalidMetadata = errors.New("invalid metadata")
 | 
						|
)
 | 
						|
 | 
						|
// Session is how this package sees a Session.
 | 
						|
type Session interface {
 | 
						|
	DefaultHTTPClient() model.HTTPClient
 | 
						|
	KeyValueStore() model.KeyValueStore
 | 
						|
	Logger() model.Logger
 | 
						|
	ProxyURL() *url.URL
 | 
						|
	UserAgent() string
 | 
						|
}
 | 
						|
 | 
						|
// Client is a client for the OONI probe services API.
 | 
						|
type Client struct {
 | 
						|
	httpx.APIClientTemplate
 | 
						|
	LoginCalls    *atomicx.Int64
 | 
						|
	RegisterCalls *atomicx.Int64
 | 
						|
	StateFile     StateFile
 | 
						|
}
 | 
						|
 | 
						|
// GetCredsAndAuth is an utility function that returns the credentials with
 | 
						|
// which we are registered and the token with which we're logged in. If we're
 | 
						|
// not registered or not logged in, an error is returned instead.
 | 
						|
func (c Client) GetCredsAndAuth() (*LoginCredentials, *LoginAuth, error) {
 | 
						|
	state := c.StateFile.Get()
 | 
						|
	creds := state.Credentials()
 | 
						|
	if creds == nil {
 | 
						|
		return nil, nil, ErrNotRegistered
 | 
						|
	}
 | 
						|
	auth := state.Auth()
 | 
						|
	if auth == nil {
 | 
						|
		return nil, nil, ErrNotLoggedIn
 | 
						|
	}
 | 
						|
	return creds, auth, nil
 | 
						|
}
 | 
						|
 | 
						|
// NewClient creates a new client for the specified probe services endpoint. This
 | 
						|
// function fails, e.g., we don't support the specified endpoint.
 | 
						|
func NewClient(sess Session, endpoint model.OOAPIService) (*Client, error) {
 | 
						|
	client := &Client{
 | 
						|
		APIClientTemplate: httpx.APIClientTemplate{
 | 
						|
			BaseURL:    endpoint.Address,
 | 
						|
			HTTPClient: sess.DefaultHTTPClient(),
 | 
						|
			Logger:     sess.Logger(),
 | 
						|
			UserAgent:  sess.UserAgent(),
 | 
						|
		},
 | 
						|
		LoginCalls:    &atomicx.Int64{},
 | 
						|
		RegisterCalls: &atomicx.Int64{},
 | 
						|
		StateFile:     NewStateFile(sess.KeyValueStore()),
 | 
						|
	}
 | 
						|
	switch endpoint.Type {
 | 
						|
	case "https":
 | 
						|
		return client, nil
 | 
						|
	case "cloudfront":
 | 
						|
		// Do the cloudfronting dance. The front must appear inside of the
 | 
						|
		// URL, so that we use it for DNS resolution and SNI. The real domain
 | 
						|
		// must instead appear inside of the Host header.
 | 
						|
		URL, err := url.Parse(client.BaseURL)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		if URL.Scheme != "https" || URL.Host != URL.Hostname() {
 | 
						|
			return nil, ErrUnsupportedCloudFrontAddress
 | 
						|
		}
 | 
						|
		client.APIClientTemplate.Host = URL.Hostname()
 | 
						|
		URL.Host = endpoint.Front
 | 
						|
		client.BaseURL = URL.String()
 | 
						|
		if _, err := url.Parse(client.BaseURL); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		return client, nil
 | 
						|
	default:
 | 
						|
		return nil, ErrUnsupportedEndpoint
 | 
						|
	}
 | 
						|
}
 |