ooni-probe-cli/internal/oonirun/v1.go

109 lines
3.0 KiB
Go
Raw Permalink Normal View History

package oonirun
//
// OONI Run v1 implementation
//
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
)
var (
// ErrInvalidV1URLScheme indicates a v1 OONI Run URL has an invalid scheme.
ErrInvalidV1URLScheme = errors.New("oonirun: invalid v1 URL scheme")
// ErrInvalidV1URLHost indicates a v1 OONI Run URL has an invalid host.
ErrInvalidV1URLHost = errors.New("oonirun: invalid v1 URL host")
// ErrInvalidV1URLPath indicates a v1 OONI Run URL has an invalid path.
ErrInvalidV1URLPath = errors.New("oonirun: invalid v1 URL path")
// ErrInvalidV1URLQueryArgument indicates a v1 OONI Run URL query argument is invalid.
ErrInvalidV1URLQueryArgument = errors.New("oonirun: invalid v1 URL query argument")
)
// v1Arguments contains arguments for a v1 OONI Run URL. These arguments are
// always encoded inside of the "ta" field, which is optional.
type v1Arguments struct {
URLs []string `json:"urls"`
}
// v1Measure performs a measurement using the given v1 OONI Run URL.
func v1Measure(ctx context.Context, config *LinkConfig, URL string) error {
config.Session.Logger().Infof("oonirun/v1: running %s", URL)
pu, err := url.Parse(URL)
if err != nil {
return err
}
switch pu.Scheme {
case "https":
if pu.Host != "run.ooni.io" {
return ErrInvalidV1URLHost
}
if pu.Path != "/nettest" {
return ErrInvalidV1URLPath
}
case "ooni":
if pu.Host != "nettest" {
return ErrInvalidV1URLHost
}
if pu.Path != "" && pu.Path != "/" {
return ErrInvalidV1URLPath
}
default:
return ErrInvalidV1URLScheme
}
name := pu.Query().Get("tn")
if name == "" {
return fmt.Errorf("%w: empty test name", ErrInvalidV1URLQueryArgument)
}
var inputs []string
if ta := pu.Query().Get("ta"); ta != "" {
inputs, err = v1ParseArguments(ta)
if err != nil {
return err
}
}
if mv := pu.Query().Get("mv"); mv != "1.2.0" {
return fmt.Errorf("%w: unknown minimum version", ErrInvalidV1URLQueryArgument)
}
exp := &Experiment{
Annotations: config.Annotations,
ExtraOptions: nil, // no way to specify with v1 URLs
Inputs: inputs,
InputFilePaths: nil,
MaxRuntime: config.MaxRuntime,
Name: name,
NoCollector: config.NoCollector,
NoJSON: config.NoJSON,
Random: config.Random,
ReportFile: config.ReportFile,
Session: config.Session,
newExperimentBuilderFn: nil,
newInputLoaderFn: nil,
newSubmitterFn: nil,
newSaverFn: nil,
newInputProcessorFn: nil,
}
return exp.Run(ctx)
}
// v1ParseArguments parses the `ta` field of the query string.
func v1ParseArguments(ta string) ([]string, error) {
var inputs []string
pa, err := url.QueryUnescape(ta)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrInvalidV1URLQueryArgument, err.Error())
}
var arguments v1Arguments
if err := json.Unmarshal([]byte(pa), &arguments); err != nil {
return nil, fmt.Errorf("%w: %s", ErrInvalidV1URLQueryArgument, err.Error())
}
inputs = arguments.URLs
return inputs, nil
}