refactor+doc(oonimkall): improve docs and simplify code (#253)
This diff has been extracted from a larger diff written for https://github.com/ooni/probe/issues/1346
This commit is contained in:
parent
fbee736e90
commit
78b5bf0caa
|
@ -4,11 +4,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
engine "github.com/ooni/probe-cli/v3/internal/engine"
|
"github.com/ooni/probe-cli/v3/internal/engine"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/atomicx"
|
"github.com/ooni/probe-cli/v3/internal/engine/atomicx"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/probeservices"
|
"github.com/ooni/probe-cli/v3/internal/engine/probeservices"
|
||||||
|
@ -21,7 +20,7 @@ type AtomicInt64 struct {
|
||||||
*atomicx.Int64
|
*atomicx.Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following two variables contain metrics pertaining to the number
|
// These two variables contain metrics pertaining to the number
|
||||||
// of Sessions and Contexts that are currently being used.
|
// of Sessions and Contexts that are currently being used.
|
||||||
var (
|
var (
|
||||||
ActiveSessions = &AtomicInt64{atomicx.NewInt64()}
|
ActiveSessions = &AtomicInt64{atomicx.NewInt64()}
|
||||||
|
@ -33,11 +32,22 @@ var (
|
||||||
// to this instance in the SessionConfig object. All log messages that
|
// to this instance in the SessionConfig object. All log messages that
|
||||||
// the Session will generate will be routed to this Logger.
|
// the Session will generate will be routed to this Logger.
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
|
// Debug handles debug messages.
|
||||||
Debug(msg string)
|
Debug(msg string)
|
||||||
|
|
||||||
|
// Info handles informational messages.
|
||||||
Info(msg string)
|
Info(msg string)
|
||||||
|
|
||||||
|
// Warn handles warning messages.
|
||||||
Warn(msg string)
|
Warn(msg string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExperimentCallbacks contains experiment callbacks.
|
||||||
|
type ExperimentCallbacks interface {
|
||||||
|
// OnProgress provides information about an experiment progress.
|
||||||
|
OnProgress(percentage float64, message string)
|
||||||
|
}
|
||||||
|
|
||||||
// SessionConfig contains configuration for a Session. You should
|
// SessionConfig contains configuration for a Session. You should
|
||||||
// fill all the mandatory fields and could also optionally fill some of
|
// fill all the mandatory fields and could also optionally fill some of
|
||||||
// the optional fields. Then pass this struct to NewSession.
|
// the optional fields. Then pass this struct to NewSession.
|
||||||
|
@ -83,24 +93,18 @@ type SessionConfig struct {
|
||||||
// OONI related task (e.g. geolocation). Note that the Session isn't
|
// OONI related task (e.g. geolocation). Note that the Session isn't
|
||||||
// mean to be a long living object. The workflow is to create a Session,
|
// mean to be a long living object. The workflow is to create a Session,
|
||||||
// do the operations you need to do with it now, then make sure it is
|
// do the operations you need to do with it now, then make sure it is
|
||||||
// not referenced by other variables, so the Go GC can finalize it.
|
// not referenced by other variables, so the Go GC can finalize it. This
|
||||||
//
|
// is what you would normally done with Java/ObjC.
|
||||||
// Future directions
|
|
||||||
//
|
|
||||||
// We will eventually rewrite the code for running new experiments such
|
|
||||||
// that a Task will be created from a Session, such that experiments
|
|
||||||
// could share the same Session and save geolookups, etc. For now, we
|
|
||||||
// are in the suboptimal situations where Tasks create, use, and close
|
|
||||||
// their own session, thus running more lookups than needed.
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
|
// Hooks for testing (should not appear in Java/ObjC, because they
|
||||||
|
// cannot be automatically transformed to Java/ObjC code.)
|
||||||
|
TestingCheckInBeforeNewProbeServicesClient func(ctx *Context)
|
||||||
|
TestingCheckInBeforeCheckIn func(ctx *Context)
|
||||||
|
|
||||||
cl []context.CancelFunc
|
cl []context.CancelFunc
|
||||||
mtx sync.Mutex
|
mtx sync.Mutex
|
||||||
submitter *probeservices.Submitter
|
submitter *probeservices.Submitter
|
||||||
sessp *engine.Session
|
sessp *engine.Session
|
||||||
|
|
||||||
// Hooks for testing (should not appear in Java/ObjC)
|
|
||||||
TestingCheckInBeforeNewProbeServicesClient func(ctx *Context)
|
|
||||||
TestingCheckInBeforeCheckIn func(ctx *Context)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSession creates a new session. You should use a session for running
|
// NewSession creates a new session. You should use a session for running
|
||||||
|
@ -133,6 +137,8 @@ func NewSession(config *SessionConfig) (*Session, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sess := &Session{sessp: sessp}
|
sess := &Session{sessp: sessp}
|
||||||
|
// We use finalizers to reduce the burden of managing the
|
||||||
|
// session from languages with a garbage collector.
|
||||||
runtime.SetFinalizer(sess, sessionFinalizer)
|
runtime.SetFinalizer(sess, sessionFinalizer)
|
||||||
ActiveSessions.Add(1)
|
ActiveSessions.Add(1)
|
||||||
return sess, nil
|
return sess, nil
|
||||||
|
@ -159,7 +165,9 @@ type Context struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel cancels pending operations using this context.
|
// Cancel cancels pending operations using this context. This method
|
||||||
|
// is idempotent. Calling it more than once is fine. The first invocation
|
||||||
|
// cancels the context. Subsequent invocations are no-operations.
|
||||||
func (ctx *Context) Cancel() {
|
func (ctx *Context) Cancel() {
|
||||||
ctx.cancel()
|
ctx.cancel()
|
||||||
}
|
}
|
||||||
|
@ -171,7 +179,8 @@ func (sess *Session) NewContext() *Context {
|
||||||
|
|
||||||
// NewContextWithTimeout creates an new interruptible Context that will automatically
|
// NewContextWithTimeout creates an new interruptible Context that will automatically
|
||||||
// cancel itself after the given timeout. Setting a zero or negative timeout implies
|
// cancel itself after the given timeout. Setting a zero or negative timeout implies
|
||||||
// there is no actual timeout configured for the Context.
|
// there is no actual timeout configured for the Context, making this invocation
|
||||||
|
// equivalent to calling NewContext().
|
||||||
func (sess *Session) NewContextWithTimeout(timeout int64) *Context {
|
func (sess *Session) NewContextWithTimeout(timeout int64) *Context {
|
||||||
sess.mtx.Lock()
|
sess.mtx.Lock()
|
||||||
defer sess.mtx.Unlock()
|
defer sess.mtx.Unlock()
|
||||||
|
@ -188,7 +197,7 @@ func (sess *Session) NewContextWithTimeout(timeout int64) *Context {
|
||||||
return &Context{cancel: cancel, ctx: ctx}
|
return &Context{cancel: cancel, ctx: ctx}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GeolocateResults contains the GeolocateTask results.
|
// GeolocateResults contains the results of session.Geolocate.
|
||||||
type GeolocateResults struct {
|
type GeolocateResults struct {
|
||||||
// ASN is the autonomous system number.
|
// ASN is the autonomous system number.
|
||||||
ASN string
|
ASN string
|
||||||
|
@ -203,15 +212,21 @@ type GeolocateResults struct {
|
||||||
Org string
|
Org string
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaybeUpdateResources ensures that resources are up to date.
|
// MaybeUpdateResources ensures that resources are up to date. This function
|
||||||
|
// could perform network activity when we need to update resources.
|
||||||
|
//
|
||||||
|
// This function locks the session until it's done. That is, no other operation
|
||||||
|
// can be performed as long as this function is pending.
|
||||||
func (sess *Session) MaybeUpdateResources(ctx *Context) error {
|
func (sess *Session) MaybeUpdateResources(ctx *Context) error {
|
||||||
sess.mtx.Lock()
|
sess.mtx.Lock()
|
||||||
defer sess.mtx.Unlock()
|
defer sess.mtx.Unlock()
|
||||||
return sess.sessp.MaybeUpdateResources(ctx.ctx)
|
return sess.sessp.MaybeUpdateResources(ctx.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Geolocate performs a geolocate operation and returns the results. This method
|
// Geolocate performs a geolocate operation and returns the results.
|
||||||
// is (in Java terminology) synchronized with the session instance.
|
//
|
||||||
|
// This function locks the session until it's done. That is, no other operation
|
||||||
|
// can be performed as long as this function is pending.
|
||||||
func (sess *Session) Geolocate(ctx *Context) (*GeolocateResults, error) {
|
func (sess *Session) Geolocate(ctx *Context) (*GeolocateResults, error) {
|
||||||
sess.mtx.Lock()
|
sess.mtx.Lock()
|
||||||
defer sess.mtx.Unlock()
|
defer sess.mtx.Unlock()
|
||||||
|
@ -220,7 +235,7 @@ func (sess *Session) Geolocate(ctx *Context) (*GeolocateResults, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &GeolocateResults{
|
return &GeolocateResults{
|
||||||
ASN: fmt.Sprintf("AS%d", info.ASN),
|
ASN: info.ASNString(),
|
||||||
Country: info.CountryCode,
|
Country: info.CountryCode,
|
||||||
IP: info.ProbeIP,
|
IP: info.ProbeIP,
|
||||||
Org: info.NetworkName,
|
Org: info.NetworkName,
|
||||||
|
@ -230,12 +245,17 @@ func (sess *Session) Geolocate(ctx *Context) (*GeolocateResults, error) {
|
||||||
// SubmitMeasurementResults contains the results of a single measurement submission
|
// SubmitMeasurementResults contains the results of a single measurement submission
|
||||||
// to the OONI backends using the OONI collector API.
|
// to the OONI backends using the OONI collector API.
|
||||||
type SubmitMeasurementResults struct {
|
type SubmitMeasurementResults struct {
|
||||||
|
// UpdateMeasurement is the measurement with updated report ID.
|
||||||
UpdatedMeasurement string
|
UpdatedMeasurement string
|
||||||
UpdatedReportID string
|
|
||||||
|
// UpdatedReportID is the report ID used for the measurement.
|
||||||
|
UpdatedReportID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit submits the given measurement and returns the results. This method is (in
|
// Submit submits the given measurement and returns the results.
|
||||||
// Java terminology) synchronized with the Session instance.
|
//
|
||||||
|
// This function locks the session until it's done. That is, no other operation
|
||||||
|
// can be performed as long as this function is pending.
|
||||||
func (sess *Session) Submit(ctx *Context, measurement string) (*SubmitMeasurementResults, error) {
|
func (sess *Session) Submit(ctx *Context, measurement string) (*SubmitMeasurementResults, error) {
|
||||||
sess.mtx.Lock()
|
sess.mtx.Lock()
|
||||||
defer sess.mtx.Unlock()
|
defer sess.mtx.Unlock()
|
||||||
|
@ -261,12 +281,15 @@ func (sess *Session) Submit(ctx *Context, measurement string) (*SubmitMeasuremen
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckInConfigWebConnectivity is the configuration for the WebConnectivity test
|
// CheckInConfigWebConnectivity contains WebConnectivity
|
||||||
|
// configuration for the check-in API.
|
||||||
type CheckInConfigWebConnectivity struct {
|
type CheckInConfigWebConnectivity struct {
|
||||||
CategoryCodes []string // CategoryCodes is an array of category codes
|
// CategoryCodes contains zero or more category codes (e.g. "HUMR").
|
||||||
|
CategoryCodes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a category code to the array in CheckInConfigWebConnectivity
|
// Add adds a category code to ckw.CategoryCode. This method allows you to
|
||||||
|
// edit ckw.CategoryCodes, which is inaccessible from Java/ObjC.
|
||||||
func (ckw *CheckInConfigWebConnectivity) Add(cat string) {
|
func (ckw *CheckInConfigWebConnectivity) Add(cat string) {
|
||||||
ckw.CategoryCodes = append(ckw.CategoryCodes, cat)
|
ckw.CategoryCodes = append(ckw.CategoryCodes, cat)
|
||||||
}
|
}
|
||||||
|
@ -277,36 +300,62 @@ func (ckw *CheckInConfigWebConnectivity) toModel() model.CheckInConfigWebConnect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckInConfig contains configuration for calling the checkin API.
|
// CheckInConfig contains configuration for the check-in API.
|
||||||
type CheckInConfig struct {
|
type CheckInConfig struct {
|
||||||
Charging bool // Charging indicate if the phone is actually charging
|
// Charging indicates whether the phone is charging.
|
||||||
OnWiFi bool // OnWiFi indicate if the phone is actually connected to a WiFi network
|
Charging bool
|
||||||
Platform string // Platform of the probe
|
|
||||||
RunType string // RunType
|
// OnWiFi indicates whether the phone is using the Wi-Fi.
|
||||||
SoftwareName string // SoftwareName of the probe
|
OnWiFi bool
|
||||||
SoftwareVersion string // SoftwareVersion of the probe
|
|
||||||
WebConnectivity *CheckInConfigWebConnectivity // WebConnectivity class contain an array of categories
|
// Platform is the mobile platform (e.g. "android")
|
||||||
|
Platform string
|
||||||
|
|
||||||
|
// RunType indicates whether this is an automated ("timed") run
|
||||||
|
// or otherwise a manual run initiated by the user.
|
||||||
|
RunType string
|
||||||
|
|
||||||
|
// SoftwareName is the name of the application.
|
||||||
|
SoftwareName string
|
||||||
|
|
||||||
|
// SoftwareVersion is the version of the application.
|
||||||
|
SoftwareVersion string
|
||||||
|
|
||||||
|
// WebConnectivity contains configuration items specific of
|
||||||
|
// the WebConnectivity experiment.
|
||||||
|
WebConnectivity *CheckInConfigWebConnectivity
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckInInfoWebConnectivity contains the array of URLs returned by the checkin API
|
// CheckInInfoWebConnectivity contains the WebConnectivity
|
||||||
|
// specific results of the check-in API call.
|
||||||
type CheckInInfoWebConnectivity struct {
|
type CheckInInfoWebConnectivity struct {
|
||||||
|
// ReportID is the report ID we should be using.
|
||||||
ReportID string
|
ReportID string
|
||||||
URLs []model.URLInfo
|
|
||||||
|
// URLs contains the list of URLs to measure.
|
||||||
|
URLs []model.URLInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// URLInfo contains info on a test lists URL
|
// URLInfo contains info on a specific URL to measure.
|
||||||
type URLInfo struct {
|
type URLInfo struct {
|
||||||
|
// CategoryCode is the URL's category code (e.g. "HUMR").
|
||||||
CategoryCode string
|
CategoryCode string
|
||||||
CountryCode string
|
|
||||||
URL string
|
// CountryCode is the test list from which this URL
|
||||||
|
// comes from (e.g. "IT", "FR").
|
||||||
|
CountryCode string
|
||||||
|
|
||||||
|
// URL is the URL itself.
|
||||||
|
URL string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the number of URLs.
|
// Size returns the number of URLs included into the result.
|
||||||
func (ckw *CheckInInfoWebConnectivity) Size() int64 {
|
func (ckw *CheckInInfoWebConnectivity) Size() int64 {
|
||||||
return int64(len(ckw.URLs))
|
return int64(len(ckw.URLs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// At gets the URLInfo at position idx from CheckInInfoWebConnectivity.URLs
|
// At returns the URLInfo at index idx. Note that this function will
|
||||||
|
// return nil/null if the index is out of bounds.
|
||||||
func (ckw *CheckInInfoWebConnectivity) At(idx int64) *URLInfo {
|
func (ckw *CheckInInfoWebConnectivity) At(idx int64) *URLInfo {
|
||||||
if idx < 0 || int(idx) >= len(ckw.URLs) {
|
if idx < 0 || int(idx) >= len(ckw.URLs) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -323,20 +372,27 @@ func newCheckInInfoWebConnectivity(ckw *model.CheckInInfoWebConnectivity) *Check
|
||||||
if ckw == nil {
|
if ckw == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := new(CheckInInfoWebConnectivity)
|
return &CheckInInfoWebConnectivity{
|
||||||
out.ReportID = ckw.ReportID
|
ReportID: ckw.ReportID,
|
||||||
out.URLs = ckw.URLs
|
URLs: ckw.URLs,
|
||||||
return out
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckInInfo contains the return test objects from the checkin API
|
// CheckInInfo contains the result of the check-in API.
|
||||||
type CheckInInfo struct {
|
type CheckInInfo struct {
|
||||||
|
// WebConnectivity contains results that are specific to
|
||||||
|
// the WebConnectivity experiment. This field MAY be null
|
||||||
|
// if the server's response did not contain any info.
|
||||||
WebConnectivity *CheckInInfoWebConnectivity
|
WebConnectivity *CheckInInfoWebConnectivity
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckIn function is called by probes asking if there are tests to be run
|
// CheckIn calls the check-in API. Both ctx and config MUST NOT be nil. This
|
||||||
// The config argument contains the mandatory settings.
|
// function will fail if config is missing required settings. The return value
|
||||||
// Returns the list of tests to run and the URLs, on success, or an explanatory error, in case of failure.
|
// is either an error or a valid CheckInInfo instance. Beware that the returned
|
||||||
|
// object MAY still contain nil fields depending on the server's response.
|
||||||
|
//
|
||||||
|
// This function locks the session until it's done. That is, no other operation
|
||||||
|
// can be performed as long as this function is pending.
|
||||||
func (sess *Session) CheckIn(ctx *Context, config *CheckInConfig) (*CheckInInfo, error) {
|
func (sess *Session) CheckIn(ctx *Context, config *CheckInConfig) (*CheckInInfo, error) {
|
||||||
sess.mtx.Lock()
|
sess.mtx.Lock()
|
||||||
defer sess.mtx.Unlock()
|
defer sess.mtx.Unlock()
|
||||||
|
@ -348,14 +404,14 @@ func (sess *Session) CheckIn(ctx *Context, config *CheckInConfig) (*CheckInInfo,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if sess.TestingCheckInBeforeNewProbeServicesClient != nil {
|
if sess.TestingCheckInBeforeNewProbeServicesClient != nil {
|
||||||
sess.TestingCheckInBeforeNewProbeServicesClient(ctx)
|
sess.TestingCheckInBeforeNewProbeServicesClient(ctx) // for testing
|
||||||
}
|
}
|
||||||
psc, err := sess.sessp.NewProbeServicesClient(ctx.ctx)
|
psc, err := sess.sessp.NewProbeServicesClient(ctx.ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if sess.TestingCheckInBeforeCheckIn != nil {
|
if sess.TestingCheckInBeforeCheckIn != nil {
|
||||||
sess.TestingCheckInBeforeCheckIn(ctx)
|
sess.TestingCheckInBeforeCheckIn(ctx) // for testing
|
||||||
}
|
}
|
||||||
cfg := model.CheckInConfig{
|
cfg := model.CheckInConfig{
|
||||||
Charging: config.Charging,
|
Charging: config.Charging,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user